0

I want to make some directory trees and populate each end of the tree with a text file with random content.

Here is what I tried so far:

#!/bin/bash

set -e

echo "Generating test folders"

mkdir -p ./parent_{0..9}/child_{0..9}
head -c 100 /dev/urandom | xargs echo >> ./parent_{0..9}/child_{0..9}/test.txt

echo "done"

When executing, I get this error:

./gen-test.sh: line 8: ./parent_{0..9}/child_{0..9}/test.txt: ambiguous redirect

What am I doing wrong and what could be a smarter approach to this task?

1 Answer 1

1

The reason that what you're doing wrong doesn't work is because redirection and filename generation is handled by the shell, not by xargs.

When you write

foo > foo.out

then the shell will run foo for you, capture its standard output, and write that to foo.out.

If you run

foo | xargs bar > bar.out

then the shell will run foo for you, capture its standard output, run xargs, and send the standard output from foo into the standard input of xargs. At some point, xargs will run bar, and pass it the data it received on standard input as arguments, and it will do so in a way that causes the standard output of bar to be indistinguishable of the standard output of xargs. The shell will capture that standard output, and write it to bar.out.

Your arguments of ./parent_{0..9}/child_{0..9}/test.txt are also interpreted by the shell. It removes that argument, and replaces it by the filenames that would be generated by that pattern, so that the mkdir command line becomes something like (... added for brevity):

mkdir -p ./parent_0/child_0/test.txt ./parent_0/child_1/test.txt ./parent_0/child_2/test.txt ... ./parent_1/child_0/test.txt ... ./parent_9/child_9/test.txt

Later on, you run

head -c 100 /dev/urandom | xargs # ...

which means you read 100 bytes of random data, turn that into command-line arguments to echo by way of xargs, which will then attempt to output that data (which is unlikely to go well, given that it is binary data and we're talking about a command line here).

Finally, the shell sees >>, which it interprets it as an appending redirect, followed by the same pattern; so that means the redirect becomes

>> /parent_0/child_0/test.txt /parent_0/child_1/test.txt # and so on

but you can't do that; you can only redirect to one file at a time (this is the "ambiguous redirect" error you see).

The easiest fix is to loop over the files:

for file in /pattern_0/child_0/test.txt; do
    head -c 100 /dev/urandom > $file
done

because any solution that uses xargs will require echo to turn command-line arguments into standard output, only to use xargs to turn the same standard output back into command-line arguments again, which is obviously inefficient.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .