I have this directory structure for about 100 users.

- user1
  - info.txt
- user2
  - info.txt
- user3
  - info.txt

Inside the info.txt files, the contents look like this.

5 some_other_info

Basically, it's just one line, containing a number, a space, then some text (may have spaces within).

I'd like to create a file result.txt that looks like this.

user1 5
user2 6
user3 7

Where user1, user2, user3, ... matches the directory name and the numbers match what's in their respective info.txt files.

You can assume that the user directories have no spaces in their names.

What's a good way to do this?

4 Answers 4

awk '{split(FILENAME,u,"/"); print u[2], $1}' users/*/info.txt

You can do this with a simple for loop.

  for userd in users/*/; do 
    if [[ -e "$userd/info.txt" ]]; then
      read num _ < "$userd/info.txt"
      printf '%s %s\n' "$user" "$num"
} > out.txt
  • There's no need to wrap this in curly braces.
    – Chris Down
    Commented Oct 9, 2013 at 7:44

A variation on Stephane's answer:

$ cd users && \
      find -mindepth 2 -maxdepth 2 -type f -name info.txt  \
    | xargs awk '{ split(FILENAME,u,"/"); print u[2], $1 }'

The use of (GNU) find and xargs protects against the number of users exceeding the maximal number of program arguments (which is system dependent).

(xargs will correctly split the input and repeatedly call awk if necessary)

for f in users/*/info.txt; do
    set -- $(< $f)

    # choose one of:
    user=$(basename $(dirname $f))
    # or
    dir=${f%/*}; user=${dir##*/}

    echo $user $num
  • Using set to split the string is interesting. I like that approach.
    – jordanm
    Commented Oct 8, 2013 at 21:59
  • @jordanm This has undesirable consequences in certain rare circumstances. For example, if some_other_info is /auto/someserver/*, this could trigger some automounting. Commented Oct 8, 2013 at 22:32

You must log in to answer this question.

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