Problems with command substitution and whitespaces
Hi,
i have a text file with a lot filenames in it. The files listed in the file shall be deleted. Each filename is on a separate line. To delete the files I thought I can do something like that: Code:
rm `cat files_to_be_deleted.txt` Code:
for file in `cat files_to_be_deleted.txt`; do rm $file; done So I tried to quote all filenames in the file this way "file to be deleted" but this doesn't change the behaviour that the filename is cutted at the whitespaces. I also tried to quote the whitespaces with backslashes in the file but this also doesn't change the behaviour. Can someone help me, to understand the behaviour and show me the right way, please? Thanks! -integrale16 |
Yes, i have run into this issue in the past too, and there are at least three different ways I have used to get around this and i'm sure there are more out there, however, i will just list the easiest way here:
Code:
cat files_to_be_deleted.txt | while read filename -RN. |
@RN: Thanks a lot for the alternative solution!
But is there someone out there, who is able and willing to explain me, why it doesn't work in the way I tried it, because I would like to understand it. As far as I recognized, the quotation marks "" and also the backslash are passed to the next command (i.e. for), but obviously not interpreted the way I would expect. Although the quotation is passed to the next command, the argument is cutted at the whitespaces. Would be happy if someone could explain it to me. -integrale16 |
Quote:
Without the double quotes, when bash finds $file, it substitutes the value of $file. If the value of $file is "some stupid file name with spaces in the name", bash then "tokenises" it into individual words as delimited by the spaces. Finally bash runs the command and rm is asked to remove not one file but several files: some, stupid, ... and name. When double quotes are used they tell bash not to tokenise the value of $file but to take it as a single token and rm is asked to remove a single file named "some stupid file name with spaces in the name". For more info on quoting see the GNU Bash Reference Manual |
@catkin:
I think you misunderstood me. In general it's clear to me that if I write Code:
rm some stupid file name Code:
rm "some stupid file name" Code:
rm some\ stupid\ file\ name But my problem is, that I have a textfile which containes a list of filenames which I want to delete. Therefore I tried this Code:
rm `cat textfile` "some stupid file name" I also tried to quote the filenames this way some\ stupid\ file\ name. This gives the arguments: some\ stupid\ file\ name What I try to understand now is, why the qoutes are handed over to rm but don't take effect? The solution posted from RN, works without any problems. To me it seems it has something to do with the command substitution because the solution with the pipe works fine. I hope it's clear now, what my problem is and what I want to know respectively try to understand. -integrale16 |
Quote:
Code:
cat filename | while read path The remainder of the script must guard against tokenization too: Code:
cat filename | while read path |
Lookup the IFS var; http://tldp.org/LDP/abs/html/special-chars.html
The default internal field separator http://tldp.org/LDP/abs/html/interna...es.html#IFSREF any num of spaces or tabs or a newline One soln is to temporarily set it to newline only Code:
OLDIFS=$IFS # save old val : optional |
@lutusp:
Thanks for your explanation. I think, it's (more or less) clear for me, how the script with cat and while works. I would like to know now, why Code:
rm `cat files_to_be_deleted` At the moment my explanation would be that the command cat tokenized the input from the textfile. I think cat reads not whole lines but character by character, right? And the output of cat goes directly to the command rm and is not seen or interpreted by the bash, so that the quotation doesn't take effect, also right? The thing why the solution with cat, while and read works is, that read collects the data from a whole line before it hands it over to the variable, right? Would be pleased, if someone could tell me if I catched that right, thanks. @chrism01: Thanks for your solution. I also thought about something like this, but was not sure how to do it :-) -integrale16 |
If you read those links, they'll explain it. Also, most *nix cmds take 'whitespace' as the default param separator, you just have to keep that in mind.
Cat will rtn space separated words in that context. |
Quote:
Quote:
Quote:
Quote:
For the full story of how bash expands command lines (many steps, specific sequence) see the GNU Bash Reference. |
Quote:
If it's like you tell me, that the cat command is executed an then the bash replaces the command with it's output, why doesn't it see or better care about the "" qoutes. Let's assume, the file has this content: "filename 1" "filename 2" "filename 3" Replacing the cat command with it's output, in my eyes would give: rm "filename 1" "filename 2" "filename 3" Then the command should work as I would expect it. But the resulting command line works something like this (in principle, not verbatim): rm '"filename' '1"' '"filename' '2"' '"filename' '3"' or rm "filename rm 1" rm "filename rm 2" rm "filename rm 3" Maybe I will never catch this :-( -integrale16 |
Quote:
Code:
rm whatever there was in your file and the next line and the following one For bash, the default value of the separator ($IFS) includes three special characters: blank, TAB and carry return. *ALL* of these are separators, and are treated as such when not quoted or escaped. So, your loop will act once for each token, and a token is either the first element, the last element, or anything else that goes in between two separators of any kind (again: tab, space or CR). As I told you, the whole deal is about IFS, so, you can very well define your own IFS and that way you will get along with your original idea, in command line it would look like this: Code:
$ IFS='<here you just press ENTER> Quote:
Quote:
Code:
cat file | while read foo The key difference is that in a for loop bash gets in the middle and the output is already broken into tokens when it reaches the tools. On the contrary, with "cat | while read" clause, the data is streamed directly from cat into read, so the usual shell rules do not apply. In the first case, bash read something, cut it into tokens and then pass it to the rm command, in the second case cat sends the data to read, and bash is not in the middle to cut or tokenize anything. Read will eat data from cat until he reads a CR. In whatever case, and with independence on how you filled the variable, you have to be careful to quote that variable when you want to read its contents. Code:
cat file | while read foo; do whatever with "$foo"; done |
All times are GMT -5. The time now is 09:45 AM. |