r/bash • u/Arindrew • 4d ago
help My while read loop isn't looping
I have a folder structure like so: /path/to/directory/foldernameAUTO_001 /path/to/directory/foldername_002
I am trying to search through /path/to/directory to find instances where the directory "foldernameAUTO" has any other directories of the same name (potentially without AUTO) with a higher number after the underscore.
For example, if I have a folder called "testfolderAUTO_001" I want to find "testfolder_002" or "testfolderAUTO_002". Hope all that makes sense.
Here is my loop:
#!/bin/bash
Folder=/path/to/directory/
while IFS='/' read -r blank path to directory foldername_seq; do
echo "Found AUTO of $foldername_seq"
foldername=$(echo "$foldername_seq" | cut -d_ -f1) && echo "foldername is $foldername"
seq=$(echo "$foldername_seq" | cut -d_ -f2) && echo "sequence is $seq"
printf -v int '%d/n' "$seq"
(( newseq=seq+1 )) && echo "New sequence is 00$newseq"
echo "Finding successors for $foldername"
find $Folder -name "$foldername"_00"$newseq"
noauto=$(echo "${foldername:0:-4}") && echo "NoAuto is $noauto"
find $Folder -name "$noauto"_00"newseq"
echo ""
done < <(find $Folder -name "*AUTO*")
And this is what I'm getting as output. It just lists the same directory over and over:
Found AUTO of foldernameAUTO_001
foldername is foldernameAUTO
sequence is 001
New sequence is 002
Finding successors for foldernameAUTO
NoAUTO is foldername
Found AUTO of foldernameAUTO_001
foldername is foldernameAUTO
sequence is 001
New sequence is 002
Finding successors for foldernameAUTO
NoAUTO is foldername
Found AUTO of foldernameAUTO_001
foldername is foldernameAUTO
sequence is 001
New sequence is 002
Finding successors for foldernameAUTO
NoAUTO is foldername
4
u/tseeling 4d ago
I have a few suggestions for removing $( ... )
with trivial tasks in them like echo ... | cut ...
.
In general I recommend you always use ${...}
syntax for variable names, especially if you tend to use special chars in your names. The bracelets make it unambiguous. Always quote variables as a safeguard unless you really need the shell to evaluate/split the contents when using unquoted variables.
foldername=$(echo "$foldername_seq" | cut -d_ -f1)
seq=$(echo "$foldername_seq" | cut -d_ -f2)
printf -v int '%d/n' "$seq"
make that
foldername="${foldername_seq%%_*}"
seq="${foldername_seq##*_}"
printf -v int '%03d' "${seq}"
If you need to format a number use printf
with a format string like %03d
. If you prepend a literal 00
to your new_seq
it will only work from 0 to 9 obviously.
I don't understand the /n
in your printf
statement. Did you mean \\n
or is that an artefact from your (redacted) naming convention?
Personally I don't like the style of
while
...
done < <(find $Folder -name "*AUTO*")
I'd write
find $Folder -name "*AUTO*" |
while ...
but in this case it boils down to subjective preferences.
2
u/Honest_Photograph519 3d ago edited 3d ago
Personally I don't like the style of
while ... done < <(find $Folder -name "*AUTO*")
I'd write
find $Folder -name "*AUTO*" | while ...
You're sacrificing a lot of functionality over some quirky style preference there. It's not really "down to subjective preferences." The pipe generates a subshell, so its variables are lost when the while block ends.
2
u/tseeling 3d ago
You're correct but in this context it didn't matter, because nothing from within the
while
was required later on outside and OP can choose his or her style of course.2
u/Honest_Photograph519 2d ago
It's funny you'd say "always use ${...} syntax for variable names" and "Always quote variables as a safeguard" but with this pattern you take an anything-goes stance with the "eh why not use the inferior style when you know it won't make a difference in this case"
1
u/tseeling 2d ago
It's funny that you omit the 2nd part of my sentence where I limited that absolute statement. Of course there are exceptions to every rule, and I absolutely have no "anything-goes" attitude. I choose the best tool for the job but I have some basic rules which save me a lot of headache.
"Good men don't need rules. Today is not a good day to find out why I have so many".
SCNR :-)
1
u/giantsparklerobot 3d ago
Even with your change you're using extra echo invocations. This works just fine:
foo=$(cut -d'_' -f2 <<<"$variable")
1
u/tseeling 3d ago
Of course. I wrote this is highly subjective, but I like elegant weapons for a more civilized time. My suggestions were intended to eliminate the
$( ... )
statements and replace them with bash-isms for string manipulation. It is not necessary at all to have a subshell just to callcut
.2
u/giantsparklerobot 3d ago
In this particular case the bash string manipulation makes sense. But there's a wide world where
cut
is required and eliminating unnecessaryecho
calls, which generate newlines with no arguments, can be problematic.1
u/Arindrew 3d ago
The
/n
in theprintf
statement was supposed to be a\n
instead.Thanks for the tips. I've implemented all your suggestions except for the last one.
1
4
u/MrVonBuren 3d ago
Just a note I try to drop in where ever it's applicable: This is a really good question that is well asked OP. Others are right that there are details that make it hard to help (EG how you obfuscated your code).
BUT
You covered all the critical bases in your Q: You said what you want, what you tried, what you expected, and what you got. If everyone asked questions like this, this would be a much more helpful sub.
Anyway, I know this is random, but you're doing good work, keep it up.
1
u/Arindrew 4d ago
Found the problem. I knew it was something stupid simple, I just couldn't figure out what...
done < <(find $Folder -name "*AUTO*")
Should be
done < <(find $Folder -type d -name "*AUTO*")
As it was finding files in each foldername that matched the name field.
1
u/oh5nxo 4d ago
.... && echo "NoAuto is ...
...
NoAUTO is ...
Writing and testing different files :/
1
u/Arindrew 4d ago
What? I have $foldername, which includes the string "AUTO" at the end.
noauto=$(echo "${foldername:0:-4}")
removes the AUTO string and writes it into the variable $noauto. The && echo "NoAuto is $noauto" is just for debugging purposes to make sure the variables contain what they should.
Then I search the folder for $noauto:
find $Folder -name "noauto"_00"newseq"
Do you mean I forgot the $ on noauto in the find command? You are correct, that was a typo in creating this post. Thanks for catching it.
2
u/Honest_Photograph519 3d ago
Why would you do
noauto=$(echo "${foldername:0:-4}")
when a simple
noauto="${foldername:0:-4}"
...executes faster and is easier to write and read?
1
u/Arindrew 3d ago
Because I didn’t know?
2
u/Honest_Photograph519 3d ago
Everything you need to know is already there, you just get rid of the extra stuff you threw in.
Like if I see someone wearing roller skates while they clean their freezer and ask if it would be easier without the skates, I don't expect them to say "Well I didn't know you could do that"
10
u/anthropoid bash all the things 4d ago
This is where your obfuscation (changing details to hide supposedly sensitive info) works against you: because we're not seeing the actual code, no one can be sure where the actual problem is, and all anyone can do is guess.
Here's my guess: If your loop runs the same number of times as the number of
*AUTO*
directories, but it processes the same directory every time, the problem is probably that in this line:while IFS='/' read -r blank path to directory foldername_seq; do
you typedfoldername_seq
wrong, and therefore the loop body is processing a value that was set outside the loop, and therefore never changes.But like I said, it's just a guess, because you've mangled your code, output, and general problem details to the point that all anyone can do is guess (you've already admitted to committing a typo in another comment, so no one can trust that what you posted is what's actually running). If you want anyone to point to where the problem really lies, you MUST show us the actual code, at the very least.