giobrach giobrach - 1 month ago 14
Bash Question

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?

Answer Source

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