I'm trying to write a simple bash script to count the number of files in a directory, and then add a new file with name file<#files> to the end of the directory. My current attempt is:
name="out"
num=$(ls -l|wc -l)
echo foo > "${name}${num}"
However, this gives me a bunch of spaces, resulting in the filename out      12. Why do the spaces appear, and how do I concatenate these strings without creating spaces?
On OS X (and BSD-like systems in general), wc -l left-space-pads the number to 8 characters, e.g., _______7 (_ representing a space here for technical reasons).
(If you use wc's output unquoted with echo - e.g.,  echo $(wc -l <<<'dummy'), you will not see the padding, because the shell will "eat" the leading spaces; if you double-quote the command substitution, you'll see them:  echo "$(wc -l <<<'dummy')"
That said, you're better off using neither ls nor wc - for both reasons of robustness and performance; use globbing to capture all filenames in an array and use that array's element count instead:
name='out'
files=( * ) # collect all filenames in array
echo foo > "${name}${#files[@]}" # use array-element count
Note: By default, * will not include hidden items, just like the OP's ls command (because it doesn't include -A or -a).
Use shopt -s dotglob to include hidden items too.
Also, if there happen to be no matching items at all, Bash will return the pattern as-is, i.e., *; to have Bash return the empty string instead, use shopt -s nullglob.
Generally, if you do find yourself needing to trim leading and trailing whitespace from command output and/or a Bash variable:
To trim a value stored in a Bash variable, use read:
 # Single-line value:
 val='  a   b  '
 read -r val <<<"$val" # $val now contains 'a   b'
 # Multi-line value:
 # Note: Trims leading and trailing whitespace including newlines, but 
 # preserves *any* interior whitespace, including empty and all-whitespace lines.
 val=$'\n \t  one\n  \ntwo\n  \n'
 read -r -d '' val <<<"$val" # $val now contains $'one\n  \ntwo'
To trim command output as part of a pipeline or lines from stdin / a file:
If the value to trim has either no interior whitespace or you want to ensure / don't mind that interior whitespace is normalized to a single space each, pipe to xargs (this works, because xargs, after parsing the input into words, passes them to the echo utility by default - without shell involvement); the result is always a single output line; caveat: xargs removes embedded quotes and \ instances, unless you \-escape them:
val=$(ls | wc -l | xargs) # $val now contains trimmed count
If preserving line-interior whitespace as-is matters, use sed; note that - unlike read -r d '' above - this is a LINE-based solution, so the number of input lines will be preserved, including leading and trailing empty or all-whitespace lines, with all-whitespace lines trimmed to empty ones (assumes GNU Sed or BSD Sed):
echo $'\nfoo \n \n bar \n' | sed -E 's/^[[:blank:]]+|[[:blank:]]+$//g'
# -> $'\nfoo\n\nbar'
Simplified scenario: Remove ALL whitespace from a value, using tr:
single-line / per-line:
tr -d '[:blank:]' <<<$'  a   b     c \n foo  ' # -> $'abc\nfoo\n'
globally, including newlines:
tr -d '[:space:]' <<<$'  a   b     c \n foo  ' # -> 'abcfoo'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With