[SOLVED] printf in nested for/while loop not working
Linux - NewbieThis Linux forum is for members that are new to Linux.
Just starting out and have a question?
If it is not in the man pages or the how-to's this is the place!
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
For a given two sets of keywords ( say set1 is {keyA1, keyA2} and set2 is {keyB1, keyB2} ) my objective is to create list of file pairs fileA (files which have all the keyA words in its path) and fileB (files which have all the keyA words in its path) by searching recursively.
I wrote following code but it is not printing out the filename as expected. If condition is there to avoid repetitions and pair with itself, however, I have commented out.
Can someone point out the mistake/ improvement ?
Code:
#!/bin/bash
keyA1="AC"
keyA2="LM"
keyB1="SR"
keyB2="LT"
find . -name "*.txt" -path "*$keyA*" -path "*$keyA2*" | while read -d $'\0' fileA
do
# printf '%s %s \n' "fileA: " "$fileA"
find . -name "*.txt" -path "*$keyB1*" -path "*$keyB2*" | while read -d $'\0' fileB
do
# if [ $fileA \< $fileB ] ; then
printf '%s %s \n' "$fileA" "$fileB"
# fi
done
done
Earlier I observed that if I placed the printf statement right after the first while loop (commented out in the above code) it makes the innermost printf functional on some machines (Cygwin). This make me wonder if this is similar and related to fflush() in C.
and how are the two finds related to each other? I could not understand it. You repeat the same command again and again (the second find).
I would recommend you another solution.
I think read -d $'\0' would require -print0 option to find.
Thanks very much MadeinGermany. All the points you mentioned were helpful. *$keyA* was in fact a typo. I edited my code with the points you mentioned and it is working fine now.
I guess what made the difference is the substitution by "while read -r fileA" in the code.
Here is the working code for the reference of others:
Code:
#!/bin/bash
keyA1="AC"
keyA2="LM"
keyB1="SR"
keyB2="LT"
find . -name "*.txt" -path "*$keyA1*/*" -path "*$keyA2*/*" | while read -r fileA
do
# printf '%s %s \n' "fileA: " "$fileA"
find . -name "*.txt" -path "*$keyB1*/*" -path "*$keyB2*/*" | while read -r fileB
do
if [[ "$fileA" < "$fileB" ]] ; then
printf '%s %s \n' "$fileA" "$fileB"
fi
done
done
@grail and @pan, I appreciate your attention to my query. I did blindly copy the "while part of the command" from some online source & so I did not know the exact difference of the $'\0' syntax. May be still I don't what is raw read!
Quote:
and how are the two finds related to each other? I could not understand it. You repeat the same command again and again (the second find).
Those two are same command however with different search terms to pick respective two different file sets to be used for creating pairs.
addendum here ...
I noticed that it works only for the Ubuntu machine where I tested it.
However, in the Cygwin environment it did not work.
Individually the following command works when launched from the command prompt e.g.
Code:
find . -path "*AC/*" -path "*RM/*" -name "*.bmp" | while read -r fileA; do echo $fileA; done
However, the script, when launched, appears to run in the background for a while (may be there are many files in the file structure) and then returns to the next line on the command prompt without printing anything on the command prompt.
What means "did not work"? Error message? No output?
I did not see any output on the command prompt screen. However, I am not able to type anything. It appears that the process is running. After few seconds prompt becomes receptive and I can type there as usual.
I was curious to know what does the following redirection in your code mean ? It is that for the next loop, system is told to fetch the next line ( while read -r) from this i.e. tmpfileB as the source ?
The for-do-done is a code block. The shell reads the block into memory, at the end sees the redirection and opens (associates a file descriptor) the file for reading, and sets the code block's stdin to the file descriptor.
Then it runs the code block. Every "read" by default reads from stdin that is now the file.
I was curious though, your 'if' test, how is it relevant to test if a path to a file is less than another path to a file? Ultimately this will check the strings against each other to find
the first value which is lower based on ascii values of each character in the respective paths. I fail to see how this is a valuable test?? (or of course I am missing the point altogether )
I was curious though, your 'if' test, how is it relevant to test if a path to a file is less than another path to a file?
As MadeInGermany said above, this is to avoid the self-pair and the commutation.
Consider a matrix of size mxn where m is number of keyA files and n is the number of keyB files. One can imagine all the files path strings for keyword set keyA arranged vertically to the leftmost as row title. And those belonging to the keyword set keyB to be arranged in a 1 row vector at the top as column title. Each file pair corresponds to some location in this matrix. We want only the upper triangular matrix (avoid repetitions) and without diagonal entries (self-pair).
I would create a list of files/paths and then write a sort-like function in perl/python where I can easily reorder/count/select/whatever I want. Also using data structures may help to implement it better.
Does keyA1 occur before keyA2 (and does keyB1 occur before keyB2) in filenames, or only one of them can be found in the path (of files)?
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.