How to loop with variable as a line, instead of a word of a text?
This is INPUT:
$ ls -1 # list folder name in current folder.
the cat
the dog
the rat
$ for var in $(ls -1); do echo $var; done # the commandThis is OUTPUT that it did:
the
cat
the
dog
the
ratThis is OUTPUT that I need:
the cat
the dog
the rat 6 4 Answers
In case you are using ls only as an example to illustrate output that may contain spaces, you can try this (replace ls with your command):
ls | while IFS= read -r line ; do echo "$line"
doneWithout IFS=, the read command strips whitespace at the beginning and end of each line. Without -r, it treats \ at the end of a line of input as a line continuation character, and just removes other \ characters.
Note that ls itself defaults to one file per line when piped, even when you don't pass -1. (It's possible to have more than one line per file, though, because filenames are permitted to contain newline characters.) When piped, ls also disables color, if normally enabled.
To process a file, you can try:
while IFS= read -r line ; do echo "$line"
done < file.txtIf you want to loop over filenames, bash has the capability built-in, without parsing ls:
for file in * ; do echo "$file"
done 0 Instead of writing $(ls -1), just write *. This is a glob.
By itself, * expands to a list of files (regular files, directories, etc.) in the current directory. By default, files whose names start with . are omitted, which is also the behavior of ls and is what you usually want.
$ for f in *; do echo "$f"; done
the cat
the dog
the ratGlobbing is also called filename expansion or pathname expansion.
In general, it is best to avoid parsing the output of ls. Attempts to do so are almost always incorrect and usually quite complicated. When your goal is to extract and use the filenames as arguments to a command, as in the question asked here, it's not just difficult but in practice impossible to write a correct ls-parsing solution. The ls command produces output intended to be read by a human. Filenames can contain any character except / and the null character, even newlines. Furthermore, the most common ways people try to parse the output of ls break on extremely common filenames, like any filename that has a space in it. In this case, the difference in complexity is stark: all you need is *.
The other big thing to be careful about here is quoting. When you write a shell loop (which it itself often best avoided), expansions of the loop variable--$f in this example--should nearly always be enclosed in double quotes, as shown above. Otherwise, the shell tries to perform word splitting and (in this case, additional and unwanted) globbing. If you can't clearly explain why you're not in one of the rare scenarios where a variable expansion must be unquoted, you should quote it.
Finally, note that many commands treat arguments specially, in some or all positions, when the arguments start with -. The main reason for this is that most commands accept options introduced by -, like -l (though some commands also take - by itself to mean stdin or stdout). But it's possible for filenames to start with -. Many, though not all, commands accept -- to indicate the end of options, so that subsequent arguments will be interpreted as filenames even if they start with -. An even more broadly applicable solution is to ensure that files are specified with paths that don't start with -, for example, by expanding the glob ./* instead of *. As for echo, it doesn't treat -- specially, and the output you said you needed doesn't have the trailing ./; to be robust in the face of filename that start with - (like -n, which tells echo to suppress the final newline), you can replace echo with printf '%s\n'.
Although * has been mentioned elsewhere on this page, I think it deserves its own answer. Globbing (with *) is the only reasonable solution to the actual task this question presents. It is not an alternative to using ls with command substitution or piping the output of ls to while--because those techniques just don't do the right thing and should never be used. It is simply not feasible to make those other approaches work correctly with ls.
You should first set IFS (Internal Field Separator) to newline, then
IFS=$'\n' && for var in $(ls -1); do echo $var; done
will work
You can use mapfile to read lines from the standard input into an indexed array:
mapfile -t < <(ls -1); for i in "${MAPFILE[@]}"; do echo $i; done