2

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?

Unesix
  • 19
  • 5
  • Are the files supposed to have random names, too? Is the name the same as the random number in it? – Barmar Dec 15 '21 at 16:49
  • You are on the right track. Create a bash script first. Create a loop from 1 to 10 and print that number. Once that is successful, in that loop, write the number to a file using something like echo $NUM > ${NUM}.txt. Once you have that working, add $NUM % 2 or whatever `if` logic you prefer. chmod 644 ${NUM}.txt if it meets the if logic, otherwise chmod 640 ${NUM}.txt. Once that works, all you have to do is find a way to change from static number to random. You can use https://stackoverflow.com/questions/2556190/random-number-from-a-range-in-a-bash-script for that. – zedfoxus Dec 15 '21 at 16:54

4 Answers4

3

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.

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
2

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
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • can I use more than one condition using this model? #!/bin/bash rm -r -f *.txt for i in {1..100} do r=$RANDOM echo "$r" > "$r.txt" if (( i % 2 == 0 )) then chmod +rw "$r.txt" elif (( i % 3 == 0 )) then chmod +rwx "$r.txt" else chmod -r "$r.txt" fi done – Unesix Dec 15 '21 at 17:15
  • Of course, why would you think otherwise? – Barmar Dec 15 '21 at 17:17
1

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).

F. Hauri - Give Up GitHub
  • 64,122
  • 17
  • 116
  • 137
  • This is a few percent faster than my current version. Nice! ... but if I change to `for i in {1..100};do` it's actually about the same speed as my version. – Ted Lyngmo Dec 15 '21 at 19:14
  • I ran a multitude of tests and on my machine looping to 10000 and the `for i in {1..10000}` loop was consistently slower than the `for((i=0; i<10000; ++i)) {` version. Could be a difference in version perhaps. I used "GNU bash, version 5.1.0(1)-release (x86_64-redhat-linux-gnu)" in my tests. – Ted Lyngmo Dec 16 '21 at 07:26
  • Added performance comparison, have a look – F. Hauri - Give Up GitHub Dec 16 '21 at 07:33
  • Hmm, I can't explain the diff. I timed the whole script, creating 10000 files using the two different loops (where the only difference was the loop). Consistent results. If I shrink the script down to a loop only printing the value of `$i` (and piping the script output to `/dev/null`) the result is exactly like you've shown. Very interesting. – Ted Lyngmo Dec 16 '21 at 08:03
0

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.

RavinderSingh13
  • 130,504
  • 14
  • 57
  • 93