I need to read about 20,000 txt files on an Ubuntu machine, and insert the first line of each file in a new txt file like this:

Filename1.txt | FirstLineoftheFilename1.txt
Filename2.txt | FirstLineoftheFilename2.txt
Filename3.txt | FirstLineoftheFilename3.txt

I tried sed command and I can print the first line.

The following find command will identify the correct files:

find /db/users/logs/ -name '*.txt' -exec sed -n '1p' {} \; -exec basename {} \;

But I cant get how to combine find and sed and print the output on a single line.

Any help?

Thanks in advance!

  • Welcome to the site. Does the first line of the file need to be explicitly enclosed in " ... " in the output file? Can you edit your post to include the find command you used to localize the relevant txt files? – AdminBee May 14 '20 at 8:23
  • No, No need the " I put this to reference, explicty I need this: File1.txt | line1fromFile1.txt The command I use is this: find /db/users/logs/ -name '*.txt' -exec sed -n '1p' {} \; -exec basename {} \; It prints the first line then the basename but I cant figureout how to put together on a sinlge line on a file – Valeria Carrillo May 14 '20 at 8:26
  • Instead you can use this GNU find command: find /db/users/logs -name '*.txt' -execdir echo -n \{\} "| " \; -exec sed q \{\} \; – Rakesh Sharma May 16 '20 at 9:42

You could use GNU awk:

LC_ALL=C find /db/users/logs/ -name '*.txt' -type f -exec gawk '{
  f = FILENAME; sub(".*/", "", f)
  print f" | "$0; nextfile}' {} +

Or perl:

LC_ALL=C find . -type f -name '*.txt' -exec perl -lne '
  print $ARGV =~ s:.*/::r . " | $_"; close ARGV' {} +

Or the shell:

LC_ALL=C find /db/users/logs/ -type f -name '*.txt' -exec sh -c '
  for file do
    <"$file" IFS= read -r line || [ -n "$line" ] &&
      printf "%s\n"  "${file##*/} | $line"
  done' sh {} +

(that one will not work properly with most sh implementations if the first line contains NUL characters; though that should not happen for text files).

  • Thank you very much!! Works like a charm, I got stucked ussing this command: find /db/users/logs/ -name '*.txt' -exec awk 'END{print $0 "," FILENAME}' {} \; It prints the first line and the complete path of the file... Again, thank you very much! – Valeria Carrillo May 14 '20 at 8:47

Yet another variant:

$ find /db/users/logs/ -type f -name "*.txt" -exec \
  sh -c 'printf "%s | %s\n" "$(basename $1)" "$(head -1 $1)"' shellproc {} \;

will also handle empty files and filenames with space(s). MacOS user may not have basename available.



find /db/users/logs -type f -name '*.txt' \
    ! -empty -printf '%f | ' \
    -exec head -n 1 \{\} \;

Method ii) we use perl module File::Find which encapsulates the functionality of find command

perl -MFile::Find -e '
  find( sub { my $fh;
     -f && ! -z && /\.txt$/ and 
     open($fh, "<", $_) and 
     print("$_ | " . <$fh>) },
   shift )
' /db/users/logs

Thanks to Stephane for pointing out that basenames are required. You can do this also provided no newlines are in the filename and we can live with colon as the separator instead of pipe.

$ find /db/users/logs/  \
    -type f -name '*.txt' \
    -exec grep -Hm1 "^" {} + |
  sed 's|^/db/users/logs/\([^/]*/\)*||' 
  • 1
    Note that -m is a GNU extension, so you might as well use -H, -r and --include as well. You're missing a {}. Note that it prints the full path, not just the basename as the OP was requesting. – Stéphane Chazelas May 14 '20 at 12:44

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.