Home > Blockchain >  How to iterate files in directory with for loop in Bash
How to iterate files in directory with for loop in Bash

Time:02-23

I am trying to iterate all files in a directory with for loop:

#!/bin/bash

myname="bandit24"

cd /var/spool/$myname
echo "Executing and deleting all scripts in /var/spool/$myname:"

for file in /var/spool/bandit24/*; #  <----- here
do
    if [ "$file" != "." -a "$file" != ".." ];
    then
        echo "Handling $file"
        owner="$(stat --format "%U" ./$file)"
        if [ "${owner}" = "bandit24" ]; then
            timeout -s 9 60 ./$file
        fi
        rm -f ./$file
    fi
done

But as a result I get the following:

Executing and deleting all scripts in /var/spool/bandit24:
Handling /var/spool/bandit24/*
stat: cannot stat './/var/spool/bandit24/*': No such file or directory

The program is not trying to iterate files in /var/spool/bandit24/ directory, but it tries to iterate /var/spool/bandit24/* file itself. But I want to iterate files in /var/spool/bandit24/ directory. How to do it?

Additional question

What does cd /var/spool/$myname mean (5 line)? As I understand, it is used for specifying the directory we are going to work in, am I right?

CodePudding user response:

The for-loop is basically sound. However, if the directory is empty, the loop will be executed once, with the variable file containing the literal text /var/spool/bandit24/*.

The stat message is not from the for-loop, but from one of the commands in the loop.

The correct way would be to test if the directory is empty before you continue. You could put something like

if [ $(find . -type f | wc -l) -eq 0 ] ; then
    echo "Nothing to do"
    exit 0
fi

right after the cd.

Some other comments on your script.

  • If you do a cd in the script, you don't need to specify the full path anymore.
  • Your quoting is not really consistent. That may not be a problem if your file names never contain spaces or strange characters, but I would, for example timeout -s 9 60 "./$file" and rm -f "./file"
  • /var/spool/bandit/* will never contain . or .., so that test is useless.
  • You could also replace the test with if [ -f "$file" ] ; then

CodePudding user response:

i've a bash script in my Github repo who iterates over all files in specified directory. You can specify the deepth of search, it's something like this.

mapfile files <<< "$(find "$f" -maxdepth "$d" ! -type d)"
for file in "${files[@]}"; do
    file="$(tr -d '\n' <<< "$file")"
    # implement your logic here
done
  • $files is an array with file names
  • $f contains the directory
  • $d is the depth of search, default is 1 (only for specified folder)
  • $file contains the file name as $path_to_file/$filename

CodePudding user response:

  • What you see is the consequence of /var/spool/bandit24 being empty. In this case /var/spool/bandit24/* expands as itself. If you prefer that it expands as null you can enable the nullglob bash option before your for loop: shopt -s nullglob.

  • As variable myname is assigned bandit24, cd /var/spool/$myname is the same as cd /var/spool/bandit24. You should probably rewrite it as cd /var/spool/"$myname" || exit 1. Double quotes just in case the myname value contains spaces (it is not the case now but who knows what you will be doing next). And || exit 1 to abort your script if the directory does not exist and the cd command fails. This should avoid unwanted behaviors like executing and deleting all scripts in the current directory instead of the non-existing one...

  • with for file in /var/spool/bandit24/*, if the directory is not empty, variable file will take values like /var/spool/bandit24/foobar, not just foobar. So you cannot use it with ./$file. Solution: as you cd in the /var/spool/bandit24 directory, simply write for file in *.

  • You should really double quote all references to $file (stat --format "%U" "$file", timeout -s 9 60 ./"$file", rm -f "$file"). If you don't you take real risks.

Try the following:

#!/bin/bash

myname="bandit24"

cd /var/spool/"$myname" || exit 1
echo "Executing and deleting all scripts in /var/spool/$myname:"

shopt -s nullglob
for file in *; do
    echo "Handling $file"
    owner=$(stat --format "%U" "$file")
    if [ "$owner" = "$myname" ]; then
        timeout -s 9 60 ./"$file"
    fi
    rm -f "$file"
done

CodePudding user response:

for f in $(find . -maxdepth 1 -type f); do
    echo "current file is $f"
done
  •  Tags:  
  • bash
  • Related