user556068 user556068 - 2 months ago 9
Bash Question

Renaming multiple files to a random string renames only one file

Trying to rename files to a random string. I took some code from an answer to this question in order to generate a random string of characters.

#!/bin/bash

chars=( {a..z} {A..Z} {0..9} )

function rand_string {
local c=$1 ret=
while((c--)); do
ret+=${chars[$((RANDOM%${#chars[@]}))]}
done
printf '%s\n' "$ret"
}

output=$(rand_string 10)


For practice I made a directory at
$HOME/practice
with a few plain text files.

/Users/me/practice/testfile1.txt
/Users/me/practice/testfile2.txt
/Users/me/practice/testfile3.txt


When trying to rename these files a random string, instead of getting 3 random names, I am instead left with 1 file renamed to a random string.

for file in $HOME/practice/*
do
mv "$file" $HOME/practice/"$output"
done

#result
/Users/me/practice/i6TP3wiMDD


Replacing
mv "$file" ~/practice/"$output"
with
echo "$file" "$output"
shows me that the random string is being repeated after every file instead of generating a new random string for every file.

/Users/me/practice/testfile1.txt i6TP3wiMDD
/Users/me/practice/testfile2.txt i6TP3wiMDD
/Users/me/practice/testfile3.txt i6TP3wiMDD


My question is two part:


  1. Why is only 1 file being renamed?

  2. How can I generate a new random string for each file being renamed?



I will also say that the above random character script is above my current understanding. I know that it works for generating random characters. But the inner workings of it are still somewhat unclear to me.

Answer

The problem is you are generating only instance of the random value and storing it in the output variable and using the same to rename all the files in the loop. See the shell script expansion which comes when run with the set -x option after the defining the she-bang(#!/bin/bash).

+++ rand_string 10
+++ local c=10 ret=
+++ (( c-- ))
+++ ret+=O
+++ (( c-- ))
+++ ret+=k
+++ (( c-- ))
+++ ret+=H
+++ (( c-- ))
+++ ret+=Q
+++ (( c-- ))
+++ ret+=1
+++ (( c-- ))
+++ ret+=u
+++ (( c-- ))
+++ ret+=9
+++ (( c-- ))
+++ ret+=4
+++ (( c-- ))
+++ ret+=c
+++ (( c-- ))
+++ ret+=C
+++ (( c-- ))
+++ printf '%s\n' OkHQ1u94cC     # <--- See the same random file-names used throughout the files
++ output=OkHQ1u94cC
++ for file in '*.txt'
++ mv 1.txt OkHQ1u94cC
++ for file in '*.txt'
++ mv 2.txt OkHQ1u94cC
++ for file in '*.txt'
++ mv 5000.txt OkHQ1u94cC
++ for file in '*.txt'
++ mv 5100.txt OkHQ1u94cC
++ for file in '*.txt'
++ mv abc.txt OkHQ1u94cC
++ for file in '*.txt'
++ mv file.txt OkHQ1u94cC
++ for file in '*.txt'
++ mv final.txt OkHQ1u94cC
++ for file in '*.txt'
++ mv ini1.txt OkHQ1u94cC
++ for file in '*.txt'
++ mv ini2.txt OkHQ1u94cC
++ for file in '*.txt'
++ mv listing.txt OkHQ1u94cC
++ for file in '*.txt'
++ mv names.txt OkHQ1u94cC
++ for file in '*.txt'
++ mv parameters.txt OkHQ1u94cC
++ for file in '*.txt'
++ mv properties.txt OkHQ1u94cC
++ for file in '*.txt'
++ mv result.txt OkHQ1u94cC

The above is with my sample data. The best way is to fix the for loop in function rename by calling the function at time of expansion. Like below

#!/bin/bash

for file in $HOME/practice/*
do
    mv "$file" $HOME/practice/"$(rand_string 10)"   # <-- Here the random string length can be controlled as you need.
done

Now with the above fix, you can see my script of actual renaming of my .csv files expanded as

+++ rand_string 10
+++ local c=10 ret=
+++ (( c-- ))
+++ ret+=7
+++ (( c-- ))
+++ ret+=j
+++ (( c-- ))
+++ ret+=U
+++ (( c-- ))
+++ ret+=K
+++ (( c-- ))
+++ ret+=l
+++ (( c-- ))
+++ ret+=V
+++ (( c-- ))
+++ ret+=l
+++ (( c-- ))
+++ ret+=6
+++ (( c-- ))
+++ ret+=8
+++ (( c-- ))
+++ ret+=8
+++ (( c-- ))
+++ printf '%s\n' 7jUKlVl688
++ output=7jUKlVl688
++ for file in '*.csv'
+++ rand_string 10
+++ local c=10 ret=
+++ (( c-- ))
+++ ret+=N
+++ (( c-- ))
+++ ret+=B
+++ (( c-- ))
+++ ret+=O
+++ (( c-- ))
+++ ret+=p
+++ (( c-- ))
+++ ret+=j
+++ (( c-- ))
+++ ret+=5
+++ (( c-- ))
+++ ret+=T
+++ (( c-- ))
+++ ret+=b
+++ (( c-- ))
+++ ret+=S
+++ (( c-- ))
+++ ret+=R
+++ (( c-- ))
+++ printf '%s\n' NBOpj5TbSR
++ mv 1.csv NBOpj5TbSR                  # <- Unique file names generated.
+++ rand_string 10
+++ local c=10 ret=
+++ (( c-- ))
+++ ret+=N
+++ (( c-- ))
+++ ret+=R
+++ (( c-- ))
+++ ret+=Y
+++ (( c-- ))
+++ ret+=C
+++ (( c-- ))
+++ ret+=C
+++ (( c-- ))
+++ ret+=X
+++ (( c-- ))
+++ ret+=L
+++ (( c-- ))
+++ ret+=0
+++ (( c-- ))
+++ ret+=e
+++ (( c-- ))
+++ ret+=l
+++ (( c-- ))
+++ printf '%s\n' NRYCCXL0el
++ mv 2.csv NRYCCXL0el                    # <- Unique file names generated.