Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoid trimming of bash $() output

Tags:

bash

newline

From the Bash manual:

Bash performs the expansion by executing command and replacing the command substitution with the standard output of the command, with any trailing newlines deleted.

That means obscure bugs are possible when handling output with meaningful trailing newlines. A contrived example:

user@host:~$ path='test

'
user@host:~$ touch -- "$path"
user@host:~$ readlink -fn -- "$path"
/home/user/test

user@host:~$ full_path="$(readlink -fn -- "$path")"
user@host:~$ ls -- "$full_path"
ls: cannot access /home/user/test: No such file or directory

Any tips on how to assign the value of a command to a variable without losing semantically useful data?

The adventures of Bash continue another day!

like image 408
l0b0 Avatar asked Feb 25 '23 15:02

l0b0


2 Answers

You could use quoting and eval to work around this. Change your last two commands to:

full_path="'$(readlink -fn -- "$path"; echo \')"
eval ls -- "$full_path"

If you want the result with trailing newlines in a variable you could first add a bogus character (in this case underscore) and then remove that.

full_path="$(readlink -fn -- "$path"; echo _)"
full_path=${full_path%_}
ls -- "$full_path"
like image 112
heijp06 Avatar answered Feb 28 '23 05:02

heijp06


I have no good answers. However, this hack will work for both files with and without newlines in the name.

path='test

'
touch -- "$path"
readlink -fn -- "$path"
full_path=
if [[ $path =~ $'\n' ]] ; then
        while IFS=$'\n' read fn ; do
                full_path+="$fn"$'\n'
        done < <(readlink -fn -- "$path")
else
        full_path="$(readlink -fn -- "$path")"
fi
ls -l -- "$full_path"
like image 38
sorpigal Avatar answered Feb 28 '23 07:02

sorpigal