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?

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 Oct 9 '13 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 Oct 8 '13 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. – Gilles 'SO- stop being evil' Oct 8 '13 at 22:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.