Martin T.
Martin T.

Reputation: 547

Bash: Subshell behaviour of ls

I am wondering why I do not get se same output from:

ls -1 -tF | head -n 1

and

echo $(ls -1 -tF | head -n 1)

I tried to get the last modified file, but using it inside a sub shell sometimes I get more than one file as result?

Why that and how to avoid?

Upvotes: 3

Views: 381

Answers (2)

Martin T.
Martin T.

Reputation: 547

I just found the error: If you use echo $(ls -1 -tF | head -n 1) the file globing mechanism may result in additional matches.

So echo "$(ls -1 -tF | head -n 1)" would avoid this.

Because if the result is an executable it contains a * at the end.

I tried to place the why -F in a comment, but now I decided to put it here:

I added the following lines to my .bashrc, to have a shortcut to get last modified files or directories listed:

function L {
  myvar=$1; h=${myvar:="1"};
  echo "last ${h} modified file(s):"; 
  export L=$(ls -1 -tF|fgrep -v / |head -n ${h}| sed 's/\(\*\|=\|@\)$//g' );    
  ls -l $L;
}
function LD {
  myvar=$1;
  h=${myvar:="1"};
  echo "last ${h} modified directories:"; 
  export LD=$(ls -1 -tF|fgrep / |head -n $h | sed 's/\(\*\|=\|@\)$//g'); ls -ld $LD;
}
alias ol='L; xdg-open $L'
alias cdl='LD; cd $LD'

So now I can use L (or L 5) to list the last (last 5) modified files. But not directories.

And with L; jmacs $L I can open my editor, to edit it. Traditionally I used my alias lt='ls -lrt' but than I have to retype the name...

Now after mkdir ... I use cdl to change to that dir.

Upvotes: 0

123
123

Reputation: 11216

The problem arises because you are using an unquoted subshell and -F flag for ls outputs shell special characters appended to filenames.

-F, --classify
append indicator (one of */=>@|) to entries

Executable files are appended with *.

When you run

echo $(ls -1 -tF | head -n 1)

then

$(ls -1 -tF | head -n 1)

will return a filename, and if it happens to be an executable and also be the prefix to another file, then it will return both.

For example if you have

test.sh
test.sh.backup

then it will return

test.sh*

which when echoed expands to

test.sh test.sh.backup

Quoting the subshell prevents this expansion

echo "$(ls -1 -tF | head -n 1)"

returns

test.sh*

Upvotes: 6

Related Questions