7

Given the following ls result:

$ ls
Desktop  Documents  Downloads  Music  Pictures  Public  Templates  Videos

This will give the total number of characters for all files:

$ ls | wc -m
67

But how can I have calculate the number of characters per file name? For the same file list the result I'm after would be something like this:

8
10
10
6
etc...
slm
  • 369,824
Fabricio
  • 288

3 Answers3

8

This can be done in a very simple shell script:

for file in *; do echo -n "$file" | wc -m; done

Just loop through each file echoing the name to wc. The -n on the echo is so that it doesn't append a newline, which would erroneously increase the count by 1.

phemmer
  • 71,831
  • 1
    Perfect. Thanks. Also thanks for the echo -n tip. I had noticed the wc was counting one extra character but had not yet started dwelling on that one. – Fabricio Jan 14 '14 at 01:00
8

You don't need to call out to wc, bash is perfectly capable: ${#var} is the length of the value of $var.

for f in *; do echo ${#f}; done

reference

glenn jackman
  • 85,964
1

While @Patrick's answer is perfectly fine if you have to do a similar task over a directory tree's worth of files you'll need to change your tactics slightly. One method for handling this is to use find & while.

find & while

$ depth=2
$ find . -maxdepth $depth -type f -print0 | sed 's|\./||g' | \
    while IFS= read -r -d '' file; do \
      f=$(basename "$file"); printf "%s: %s\n" "$file" "${#f}"; \
    done | column -s : -t
dir2/more files3.txt        15
some long spacey file.txt   25
dir1/more files1.txt        15
dir1/more files2.txt        15
file 1.txt                  10
file 2.txt                  10

The above will generate a list of files separated by \0 (i.e. NULLs). You can use the variable $depth to control how deep find will look. This list is then scrubbed so that any .\ characters are removed via sed.

Lastly we loop through this list and use a printf to print each file's name out along with its length, using Bash's built-in facility to count the length of a string, ${#var}. The printf will print the file + its path but only the size of the file.

The column -s : -t is just to pretty print it. It does so by splitting the output on the colon, :, and then splitting the output up into equidistant columns.

slm
  • 369,824
  • There's no whitespace problem in Patrick's answer, not even newlines. Since the variable is quoted, there's no issue with horizontal whitespace: var=$'hello\nworld';echo -n "$var" | wc -m => 11 – glenn jackman Jan 14 '14 at 02:09
  • @glennjackman - what am I missing then? http://unix.stackexchange.com/questions/9496/looping-through-files-with-spaces-in-the-names. I know I've switched back and forth on this most annoying topic. I tried his code and you're correct BTW. – slm Jan 14 '14 at 02:22
  • 1
    The difference is for f in * -- using the wild card avoids word splitting. The shell knows you're iterating over filenames. When you do for f in $(...) you're iterating over words in text output of some program. – glenn jackman Jan 14 '14 at 11:28
  • @glennjackman - thanks, that's what I kind of figured but a bit more research and re-relearning it I now see the diff. – slm Jan 14 '14 at 13:08
  • In more detail, I guess the wildcard is expanded into filenames, and those filenames are not subjected to a 2nd round of word-splitting expansion. – glenn jackman Jan 14 '14 at 14:27
  • @glennjackman - it seems that the shell will expand the wildcard into atoms, those atoms are not subjected to any additional splitting and are viewed as split on where they matched an item to the glob. I don't know why but this still mixes me up all the time. – slm Jan 14 '14 at 15:13
  • is it possible to modify the command to output a CSV file? One column with the path and file name and a second column with the character number? – ECII Jul 29 '18 at 15:33
  • @ECII sure that would be pretty straightforward. – slm Jul 29 '18 at 17:05
  • Count you help me with it please? – ECII Jul 31 '18 at 13:40
  • @ECII - find . -maxdepth $depth -type f -print0 | sed 's|\./||g' | \ while IFS= read -r -d '' file; do \ f=$(basename "$file"); printf "%s,%s\n" "$file" "${#f}"; \ done – slm Jul 31 '18 at 13:42
  • @ECII - simply take out the | column .. at the end and change the printf to put a comma between things instead, printf "%s,%s\n". – slm Jul 31 '18 at 13:43