Is It Safe to Use "Ls" in for Loop in Bash

is it safe to use ls in for loop in bash

If you want all subdirectories in /home, use

(   # run in subshell so "shopt -s dotglob" doesn't affect rest of script
shopt -s dotglob
for DIR in /home/*/; do
DIRNAME=`basename "$DIR"`

# do whatever with "$DIR" or "$DIRNAME"
echo -n "$DIRNAME"|od -t cz
done
)

Note the trailing slash and the quoted variables.

Edit:

With directories and file

mkdir /home/..abc
mkdir /home/.abc
mkdir /home/$'a\nc'
mkdir /home/'a?c'
mkdir /home/abc
mkdir /home/'foo bar'
touch /home/test

This gives me the output

0000000   .   .   a   b   c                                              >..abc<
0000005
0000000 . a b c >.abc<
0000004
0000000 a \n c >a.c<
0000003
0000000 a ? c >a?c<
0000003
0000000 a b c >abc<
0000003
0000000 f o o b a r >foo bar<
0000007

where

ls -1 -F -b -A /home

gives me the output

..abc/
.abc/
a\nc/
a?c/
abc/
foo bar/
test

(test is a file)

ls conditional in a linux script for loop

Because the ls needs to be executed:

for i in $(ls ...); do
echo $i
done

Also, you might want to consider shell globbing instead:

for i in /home/svn/*; do
echo $i
done

... or find, which allows very fine-grained selection of the properties of items to find:

for i in $(find /home/svn -type f); do
echo $i
done

Furthermore, if you can have white space in the segments of the path or the file names themselves, use a while loop (previous example adjusted):

find /home/svn -type f|while read i; do
echo $i
done

while reads line-wise, so that the white space is preserved.

Concerning the calling of basename you have two options:

# Call basename
echo $(basename $i)
# ... or use string substitution
echo ${i##*/}

To explain the substitution: ## removes the longest front-anchored pattern from the string, # up to the first pattern match, %% the longest back-anchored pattern and % the first back-anchored full match.

Nesting if in a for loop

Two things. Never use ls to iterate files, and quote parameter expansions "$x". The for and if syntax itself is correct. I prefer to put the do and then on the same line though

for file in *; do
if [[ -d "$file" ]]; then
echo "$file is a directory"
elif [[ -f "$file" ]]; then
echo "$file is a regular file"
fi
done

For learning bash, I recommend reading http://mywiki.wooledge.org/BashGuide most other tutorials and guides are unfortunately not very good.

The reason for not doing for x in $(ls) to iterate files is because for iterates words and ls outputs lines with filenames. if those filenames happen to contain whitespace, those filenames will be split up further into words, so you'll be iterating the words of the filenames, not the filenames. Obviously, for the simple cases that works, but why use a half-working solution when there's a shorter and more elegant way that handles all cases?

With for x in * the shell replaces the * with all filenames matching that pattern in the current directory (called pathname expansion), and each filename will be a separate word so it will work no matter what characters the filename contains. Filenames can contain any character (including newlines), except / and the NUL byte (\0).

See http://mywiki.wooledge.org/ParsingLs for more on that.

As for using [[ vs [. [ is a command inherited from the bourne shell, used to test strings, files and numbers. Bash has added a more powerful [[ keyword that can do everything [ can and more. If you're writing an sh script, you must use [, but in bash scripts you should use the more powerful [[ and (( syntaxes. See http://mywiki.wooledge.org/BashFAQ/031 for more about the difference between [ and [[.

Bash Shell list files using for loop

If you're using bash 4, just use a glob:

#!/bin/bash                                                                     

shopt -s globstar

for file in **/*.txt
do
if [[ ! -f "$file" ]]
then
continue
fi
echo "$file"
# Do something else.
done

Be sure to quote "$file" or you'll have the same problem elsewhere. ** will recursively match files and directories if you have enabled globstar.

bash command - using for i in `ls` with an if statement

for i in *; do [ $i -lt 1011 ] && rm -fr $i; done

does the job.

Note: this really removes the files and does not just print the remove commands.

Loop with KSH or BASH from ls + pattern

First, you don't want "ls" in there

I'm guessing you call your function like this

myfunc FAC*

In that case, the shell expands the pattern before invoking the function. So, you actually have something like this:

myfunc FAC1 FAC2 FAC3 ...

When you use $1, you just pick the first file and ignore the rest

In your function, use this:

function myfunc {
for my_archive in "$@"; do
echo "$my_archive"
done
}

Using the quotes exactly as I have them will protect any files with spaces in the name.

Difference between using ls and find to loop over files in a bash script

The correct way to iterate over filenames here would be

for f in strain_flame_00*.dat; do
echo "$f"
mybase=$(basename "$f" .dat)
echo "$mybase"
done

Using for with a glob pattern, and then quoting all references to the filename is the safest way to use filenames that may have whitespace.

How to exit a bash loop using ls?

I suppose you can do:

#!/bin/bash
# ...
while (ls foo/* &> /dev/null); do myprog; sleep 1; done

If nothing matches foo/* (if no visible files are in directory foo), ls will fail. &> /dev/null keeps ls quiet.



Related Topics



Leave a reply



Submit