Search code examples
bashshellls

Bash command ls ordering options


New here (and to coding). I'm writing a simple bash script that helps me change a character into another character in all files of the current directory. Here's the code:

#!/bin/bash
#this script changes characters in all files of the current directory

NUM=$(ls -1 | wc -l | awk '{print $1}')

echo "There are $NUM files in this folder."
echo "Changing $1 to $2 in all their names..."
echo "Done! Here's what happened:"

for i in $(seq 1 $NUM)
do
    OLDNAME=$(ls -1 | head -$i | tail -1)
    NEWNAME=$(ls -1 | head -$i | tail -1 | sed -e "s/${1}/${2}/g")
    mv -v "$OLDNAME" "$NEWNAME"
done

So if I write ./ccin.sh " " "_" it should be changing all spaces in the names of all files in the current directory to underscores. Except it doesn't, and I think this is ls's fault. The for loop is skipping over some files because ls changes the order according to which it sorts the files every time mv effectively modifies one of them. So I would need to isolate the names of the files in a way that does not rely on the "last modified" property. I thought of file size, but that won't work if there are two files that have the same size – e.g., I'm trying this code on a directory full of empty files.

Is there a way to sort files with ls such that they remain in the same order as mv modifies their names?


Solution

  • You can try to substitute your for loop with this one:

    find . -type f | while read file
    do
        OLDNAME="$file"
        NEWNAME=`echo $file| sed -e "s/${1}/${2}/g"`
        mv -v "$OLDNAME" "$NEWNAME"
    done
    

    Instead of running ls multiple times in order to get the file to rename, you run find only once and iterate over the results