Shell - temp IFS only as a new line. Why this does not work: IFS = $ (echo -e '\ n')

I am trying to use for in a shell to iterate over file names with spaces. I have read in the question https://stackoverflow.com/a/166148/ which can be used by IFS=$'\n' and it seems to work fine. Then I read in the commentary on one of the answers , to make it posix compatible for shells other than bash, you can use IFS=$(echo -e '\n') .

The former works, but the latter does not. The comment is repeated several times. I checked the output and the variable does not seem to contain a new line.

 #!/bin/sh IFS=$(echo -e '\n') echo -n "$IFS" | od -t x1 for file in `printf 'one\ntwo two\nthree'`; do echo "Found $file" done echo IFS=$'\n' echo -n "$IFS" | od -t x1 for file in `printf 'one\ntwo two\nthree'`; do echo "Found $file" done 


The output shows that the field separator is missing for the first:

 0000000 Found one two two three 


But true for the second:

 0000000 0a 0000001 Found one Found two two Found three 


I found the answer to a question, which may or may not be a duplicate of my question:
linux - When installing IFS to split newlines into lines, why is backspace necessary? - stack overflow

Is there any danger of doing what was discussed there, IFS=$(echo -en '\n\b') ? Because it adds \b to IFS (I checked). Can \b occur in a file name? I do not want my code to mistakenly split the file name.

Do you also have recommendations for the proper handling of IFS recovery after a for loop? Should I store it in a temporary variable or run IFS and the for statement in a subshell? (and how to do it?) Thanks

+6
source share
2 answers

Refresh - change my pseudo-comment to a real answer.

I think this answer should explain the behavior you see. In particular, the $() command substitution operators and return outputs will push new lines out of the output commands. However, the direct assignment in your second example does not perform any swap commands, so it works as expected.

Therefore, I am afraid to say that, in my opinion, the above comment that you refer to is incorrect.


I think the safest way to recover IFS is to install it in a subshell. All you have to do is put the appropriate commands in parentheses () :

 ( IFS=$'\n' echo -n "$IFS" | od -t x1 for file in `printf 'one\ntwo two\nthree'`; do echo "Found $file" done ) 

Of course, calling a subshell requires a little delay, so you need to consider performance if you need to repeat it many times.


As an aside, be very careful, file names can contain both \ b and \ n. I think that only about the only characters that they cannot contain is a slash and \ 0. At least that's what he says about this wikipedia article .

 $ touch $'12345\b67890' $ touch "new > line" $ ls --show-control-chars 123467890 new line $ 
+6
source

Since newlines are devoid of command substitution, as @DigitalTrauma correctly writes, you must use a different POSIX compatible method.

 IFS=' ' 

writes a new line and assigns it. Simple and efficient.

If you don't like assignments that span more than two lines, you can use printf as an alternative.

 IFS="$(printf '\n')" 
+4
source

Source: https://habr.com/ru/post/955277/


All Articles