0

I have the following (MWE) shell script foo:

#!/bin/bash
ARGS=("$@") # all arguments
## => if it exists, we need to drop the argument "-D" here
ls -l ${ARGS[@]} | sort -fk8 

If foo is called with argument -D (the position in the list of arguments is unknown), how can I remove -D from the list of arguments? I found out that unset ARGS[${#ARGS[@]}-1] can drop the last argument, for example, but I'm not sure in which order the arguments are passed (so I first need to know at which place the argument is and then remove it in case it is provided).

6

The no-frills approach is to simply loop over the positional parameters, collecting all but -D into an array, and then use set -- to update the params:

for param; do 
    [[ ! $param == '-D' ]] && newparams+=("$param")
done
set -- "${newparams[@]}"  # overwrites the original positional params
| improve this answer | |
  • In bash, you can even do newparams+=("$param"). – choroba Nov 11 '17 at 12:13
  • Oh, yeah. I always forget if Bash has that or not. Thanks for the reminder. – B Layer Nov 11 '17 at 12:15
5

With zsh, use the ${array:#pattern} parameter expansion operator:

$ set foo -D -D bar '' $'a\nb'
$ printf '<%s>\n' "${@:#-D}"
<foo>
<bar>
<>
<a
b>

POSIXly:

for i do
  [ "$i" = -D ] || set -- "$@" "$i"
  shift
done
printf '<%s>\n' "$@"

BTW, you forgot the quotes, -- and -d:

ls -ld -- "$@"

If you want to sort by modification time, you can just use the -t option, here with -r (reverse) for oldest first:

ls -lrtd -- "$@"

Beware that if $ARGS is an empty array, it will list .. So you can do:

[ "$@" -eq 0 ] || ls -lrtd -- "$@"

To sort reliably based on the hour of the day irrespective of date, with zsh and a ls implementation that supports -U for not sorting:

zmodload zsh/stat # best in ~/.zshrc
bytime() zstat -LA REPLY -F%T  +mtime -- $REPLY
ls --full-time  -ldU -- .(e{'reply=("$@")'}o+bytime)

With limited shells like bash, it's very hard to sort files based on arbitrary metadata like that. Again, if you've got access to recent GNU tools, it's slightly easier:

[ "$#" -gt 0 ] && (
  export LC_ALL=C
  printf '%s\0' "$@" |
    sed -z 's|^-$|./-|' |
    xargs -r0 stat --printf '%y\t%n\0' -- |
    sort -zk2,2 |
    cut -zf 2-
) | xargs -r0 ls -lUd --

Portably (but still using the non-standard ls -U here), it's generally easier to resort to perl, like:

perl -MPOSIX -e '
  exec qw{ls -ldU --}, 
    map {$_->[1]}
    sort {$a->[0] cmp $b->[0]}
    map {[strftime("%T", localtime((lstat$_)[9])), $_]}
    @ARGV' -- "$@"
| improve this answer | |

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.