5

I want to be able to list files showing the number of lines the each file has and the date. I can happily get the line count using wc -l *. Not a problem. I can get the date using ls -l.

Is there a way to combine the two commands to give me a single output in columns?

7

Here is something with find + wc + date.

find . -maxdepth 1 -exec sh -c '[ -f "$0" ] && \
  printf "%6s\t\t%s\t%s\n" "$(wc -l<"$0")" "$(date -r "$0")" "$0"' {} \;

Instead of date -r one can also use for example stat -c%y.

The output looks like this:

   394      Thu Oct 16 22:38:14 UTC 2014    ./.zshrc
     7      Thu Oct 30 11:19:01 UTC 2014    ./tmp.txt
     2      Thu Oct 30 06:02:00 UTC 2014    ./tmp2.txt
    40      Thu Oct 30 04:16:30 UTC 2014    ./pp.txt

Using this as starting point one can create a function which accepts directory and pattern as parameters:

myls () { find "$1" -maxdepth 1 -name "$2" -exec sh -c '[ -f "$0" ] && \
  printf "%6s \t\t%s\t%s\n" "$(wc -l<"$0")" "$(date -r "$0")" "$0"' {} \;; }

After that myls /tmp '*.png' will list only images from /tmp (notice single quotes around pattern to prevent shell from expanding a glob operator *).

  • FYI, pay special attn to Jimmij's comment about using stat -c%y. You're running date on every file using the former command, date -r. Otherwise +1, this is a good solution to the Q. – slm Oct 30 '14 at 12:58
  • Great answer. I changed date to (date -r "$0" -%x) to give me something to import into a spreadsheet. Exactly what I needed and later I'll have a look at this more in depth. – John Reid Oct 30 '14 at 13:07
  • @JohnReid I've changed original post a little, removed unnecessary variables, and added function for patterns. You may want to take a look. @slm you are right that one can do stat and wc on the whole list find ... {} + (if this is what you are saying), yes that would be for sure faster, but on the other hand more complicated since one would need to deal with loop over arrays later on (at least as I see it). – jimmij Oct 30 '14 at 13:40
  • @jimmij: Why not say find -type f instead of making the shell do the test? – G-Man Oct 30 '14 at 17:24
  • @G-Man You are right, -type f would be simpler. – jimmij Oct 30 '14 at 18:29
2

The find-based solutions look the most elegant, but just for fun here are a couple of other ways to attack this.

This one uses sed & head to clean up the output of wc, then uses join to combine that to the output of stat, using the file name as the join field.

pat="A*";join -1 2 -2 1 -t ' ' <(wc -l $pat|head -n-1|sed 's/^[ ]*//') <(stat -c '%n %y' $pat) | awk '{printf "%-20s %6s %s %s %s\n",$1,$2,$3,$4,$5}'

And a simpler one that uses paste, with awk again tidying up the columns and also checking that the file names match.

pat="A*";paste -d ' ' <(wc -l $pat) <(stat -c '%n %y' $pat) | awk '$2==$3{printf "%6s %s %s %s\n", $1, $4, $5, $2}'

However, if the names don't match (IOW the file lists change while this command is running) then the errors are invisible. But this version reports the errors:

pat="A*";paste -d ' ' <(wc -l $pat) <(stat -c '%n %y' $pat) | awk '$2==$3{printf "%6s %s %s %s\n", $1, $4, $5, $2;next};{print "Error:" $0}'

Of course, that one will print an error message for the final line, but that's easy enough to fix. Or ignore. :)

0

I used awk as shown below:

wc -l * | head --lines=-1 | while read num file
do
  echo -n $num" "
  ls -l $file | awk '{
     for (i=6; i <= NF; i++)
       printf $i " "
  }'
  echo
done
  • head is used to remove the last line (total line of wc output).
  • num contains the number of line of file.
  • awk is used to print the time output from ls -l output.
  • (1) I realize that the question says "list files", but, if there's only one file in the directory, wc will not output a "total" line, and so the head will discard the line count of the one existing file. (2) This breaks if filenames contain newlines. (3) This breaks if filenames contain spaces, because you say $file instead of "$file". You should always quote all shell variables references unless you have a good reason not to, and you’re sure you know what you’re doing. – G-Man Oct 30 '14 at 17:21
0

I used xargs with find :

find . -type f | xargs -i  sh -c 'echo -n `date -r {}`; \
     echo -n "    "; echo -n `wc -l {}`; echo '

Output:

Thu Oct 30 19:16:38 IST 2014    0 ./b
Thu Oct 30 19:16:38 IST 2014    0 ./a
Thu Oct 30 19:16:38 IST 2014    0 ./c
Thu Oct 30 19:16:56 IST 2014    10 ./testfile

Little formatting with printf instead of echo as done by @jimmij:

find . -type f | xargs -i sh -c 'printf "%s\t%s\t\n" \
     "$(date -r {} +%x\ %H:%M:%S)" "$(wc -l {})" '
-1

What about :

ls -lrth yourfile|awk '{print $6 $7 " " $8 system("wc -l " $9) }' | tr '\n' ' '
  • Explain what this is doing and how it works! – slm Oct 30 '14 at 12:49
  • Unfortunately wc dumps a new line which is being left behind. – John Reid Oct 30 '14 at 12:50
  • I added tr to remove the "\n" – eon01 Oct 30 '14 at 12:50
  • Ah I missed it, sorry. It's not working for a list of files now though as it removes all of the line breaks. – John Reid Oct 30 '14 at 13:02
  • This seems so unnecessary given the find solution proposed above. This solution not only requires three actual commands, but it also uses a file on the system. – HalosGhost Oct 30 '14 at 13:28

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.