💾 Archived View for tilde.club › ~crmsnbleyd › working-on-input-with-spaces-bash.gmi captured on 2024-08-18 at 17:52:06. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2024-03-21)
-=-=-=-=-=-=-
Title: How to act on command output containing spaces in Bash loops
Date: 2024-02-27 14:08
Whenever you make an input in bash, it gets split into "words" based on whitespace. Usually, this is what you want, and it lets you write commands like
for x in ~/Documents ~/Downloads ~/Photos do du -h $x done
Here, all of the filenames given are interpreted separately.
If in case you want to pass the name of a string with does have spaces in it, you would usually quote it with single quotes, which makes sure the shell does not process it and takes it literally.
For example 'My Photos' is the name of a single directory and not two directories. However, the `du -h $x` command will then try to access them separately and complain about 'My' and 'Photos' not existing!
mkdir test touch test/'this has spaces' for x in $(ls test) do file test/$x done
You will get the output
test/this: cannot open `test/this' (No such file or directory) test/has: cannot open `test/has' (No such file or directory) test/spaces: cannot open `test/spaces' (No such file or directory)
You can see that the loop tries to take each space-delimited "word" as an item of the loop, which is not what we actually want. We want to treat each line of ls as an item, and not break up each individual line.
Just to be clear, I'd personally use `xargs` and do the rather silly looking
https://en.wikipedia.org/wiki/Xargs
ls ~/test/ | tr "\n" "\0" | xargs -0 printf -- 'test/%s\0' | xargs -0 file
my `xargs` does not seem to have the `--delimiter` flag, so I had to use the `-0` flag that takes the null byte as separator.
Bash uses a special variable called `$IFS` which controls how bash splits strings.
Its default value contains the space character, tab character and the newline character.
You can edit it to only be the newline character if you wish to only split on newlines (like for example in the case of ls, which gives each file/dir its own line)
To make more resilient scripts, be sure to store the previous value of `$IFS` somewhere before changing it, and revert back to that value when you're done. This will prevent nasty and confusing bugs.
# Set IFS to newline to properly handle filenames with spaces OLD_IFS=$IFS IFS=