How do I create 100 files with 1 random number in each of them and give them permissions based on the number

Viewed 456

I need to create a bash script that creates 100 files with random numbers in them. I tried to do it using:

for i in {1..100}; do $RANDOM > RANDOM.txt

I don't know if that's the correct way to do it. And then I need to give the files reading writing and execution permissions based on the number inside the file. No idea how to do that. I tried using:

if [ $i%2==0 ]
then
echo chmod +rw $RANDOM.txt

but that doesn't seem to work

Just got some feedback, it turns out I was doing everything wrong. I had to create 100 files 1.txt to 100.txt I used touch {1..100}.txt and then paste 1 random number in each of those files. Should I use echo or shuf to do this?

4 Answers

I think it'd be simplest to use chmod with octal permissions, like 0777 for rwxrwxrwx etc.

Example:

#!/bin/bash

for((i=0; i<100; ++i)) {
    rnd=$RANDOM                     # pick a random number
    (( perm = rnd % 512 ))          # make it in the range [0, 512) (0000-0777 oct)
    printf -v octperm "%04o" $perm  # convert to octal

    file=$rnd.txt                   # not sure if you meant to name it after the number
    echo $rnd > $file

    chmod $octperm $file            # chmod with octal number
}

Excerpt of files:

-r-xrw--wx 1 ted users   5 15 dec 17.53 6515.txt
---x-wxrwx 1 ted users   5 15 dec 17.53 6751.txt
-rwx-w--w- 1 ted users   5 15 dec 17.53 8146.txt
-rw-r----- 1 ted users   5 15 dec 17.53 8608.txt
--w--w---x 1 ted users   5 15 dec 17.53 8849.txt
--wx----wx 1 ted users   5 15 dec 17.53 8899.txt
--wxrwx-wx 1 ted users   5 15 dec 17.53 8955.txt
-rw-r-xrw- 1 ted users   5 15 dec 17.53 9134.txt
...

If you want to take your current umask into consideration, you could do that too, by masking away the bits in the permission indicated by the umask.

#!/bin/bash

(( um = ~$(umask) ))  # bitwise negated umask

for((i=0; i<100; ++i)) {
    rnd=$RANDOM

    (( perm = (rnd % 01000) & um ))    # [0000,0777] bitwise AND umask
    printf -v octperm "%04o" $perm

    file=$i.$rnd.txt                   # using $i. to make name unique
    echo $rnd > $file

    chmod $octperm $file
}

If your umask is currently 0022 the above example would not create any files writeable for group and/or others while the other (normal) permissions would be random.

First, you need to echo the random number, not use it as a command.

Second, if you want to use the same random number as the filename and content, you need to save it to a variable. Otherwise you'll get a different number each time you write $RANDOM.

Third, that's not how you do arithmetic and conditions inside [], any shell scripting tutorial should show the correct way. You can also use a bash arithmetic expression with (( expression )).

#!/bin/bash
for i in {1..100}
do
    r=$RANDOM
    echo "$r" > "$r.txt"
    if (( i % 2 == 0 ))
    then
        chmod +rw "$r.txt"
    fi
done

From Ted Lyngmo's answer

With some bashisms, like using integer variables properties and avoiding forks...

declare -i um=" ~$(umask) " i rnd perm
for((i=100;i--;)){
     rnd=RANDOM
     perm=' ( rnd % 01000 ) & um '
     printf -v file file-%03d-%04X.txt $i $rnd
     printf -v octperm "%04o" "$perm"
     echo "$rnd" > "$file"
     chmod "$octperm" "$file"
}

(Filename is built with file number as decimal AND random number in hexadecimal)

About performances

Maybe a little quicker, because of avoiding forks and using integers.

( The for((;;)){ ;} syntax used here is not quicker, just different (shorter)...
In fact, for ((i=100;i--;)) ;do ...;done is (insensibly) slower than for i in {1..100};do ...;done! I just wanted to use this unusual syntax for extreme bashism... ;)

Some comparison:

export TIMEFORMAT=$'(%U + %S) / \e[1m%R\e[0m : %P'

About forks, trying 1'000 variable assignment for formatting, using printf:

time for i in {1..1000};do var=$(printf "foo");done 
(0.773 + 0.347) / 1.058 : 105.93

time for i in {1..1000};do printf -v var "foo";done 
(0.006 + 0.000) / 0.006 : 99.80

From 1.5 seconds to 6 milliseconds on my host!!! There are no discussion: forks (syntax $(printf...)) is to be avoided!!

About integer properties (using 100'000 binary operations):

declare -i intvar

time for i in {1..100000};do var=$(( ( 431214 % 01000 ) & -19 ));done
(0.272 + 0.005) / 0.278 : 99.46  

time for i in {1..100000};do intvar=' ( 431214 % 01000 ) & -19 ';done 
(0.209 + 0.000) / 0.209 : 99.87  

From 0,28 seconds to 0.21 seconds, this is less significant, but.

About for i in { vs for ((i= (now using 1'000'000 loops):

time for i in {1..1000000};do :;done
(1.600 + 0.000) / 1.602 : 99.86

time for ((i=1000000;i--;));do :;done
(1.880 + 0.001) / 1.882 : 99.95

But this is clearly less significant (care about memory consumtion, using braces).

With awk, you could try following once. This program also take care of closing the open files in backend(in case we get error "too many files opened" once). Written and tested in GNU awk.

awk -v numberOfFiles="100" '
BEGIN{
  srand()
  for(i=1;i<=numberOfFiles;i++){
    out=(int(1 + rand() * 10000))
    print out > (out".txt")
    system("chmod +rw " out".txt")
    close(out)
  }
}'

I have created an awk variable named numberOfFiles where I have given 100(as per need to create 100 files), you can keep it as per your need too, in future if you need to change it and we need not to change anything in rest of program here.

Related