Bash Alias with ternary?

187 Views Asked by At

Still dipping my toes in bash coding and trying to crate a 1 liner alias to:

  • if 'perms' is entered w/out a parameter, it does a stat -c '%a - %n' *
  • else if 'perms' is entered with a parameter it does stat -c '%a - %n' <parameter>
  • Goal is if no param added, shows perms for all files/folders, else show perms for specified file/folder.

I've gotten this far with 2 different versions, but they both result in the same thing; WITH a parameter added it works fine; without a parameter it isn't getting the * added and the command says it requires a parameter. So for some reason the "if true" part is not filling the $a var, but the "else" part does fill it.

alias perms="a=$(if [ -z '$@' ]; then *; else $@; fi) stat -c '%a - %n' $a"

alias perms="a=$([[ -z '$1' ]] && '*' || $1) stat -c '%a - %n' $a"
2

There are 2 best solutions below

3
chepner On BEST ANSWER

This should be a function. (Most things people try to use aliases for should be functions.)

perms () {
  if [ $# -eq 0 ]; then
    stat -c '%a -%n' *
  else
    stat -c '%a - %n' "$@"
  fi
}
0
Ed Morton On

You should use a function. You could do something like this:

perms() { files=( * ); stat -c '%s - %n' "${@:-${files[@]}}"; }

which stores the contents of the current directory in an array files[] and then prints "$@" if populated or "${files[@]}" otherwise. The problem with that is if there are no files in your directory than * would be passed to stat literally and fail complaining there's no file named * and exit with a failure status, both of which I assume would be undesirable behavior, e.g. running in an empty directory:

$ perms() { files=( * ); stat -c '%s - %n' "${@:-${files[@]}}"; }
$ perms
stat: cannot stat '*': No such file or directory
$ echo $?
1

You could add shopt -s nullglob to try to solve that problem but then stat would fail complaining about a null argument:

$ perms() { shopt -s nullglob; files=( * ); stat -c '%s - %n' "${@:-${files[@]}}"; }
$ perms
stat: cannot stat '': No such file or directory
$ echo $?
1

so that alone doesn't solve the problem (and you'd also need to set nullglob back to it's previous value before leaving the function so that wouldn't be enough of a change anyway).

So, I'd recommend you do this instead:

$ perms() {
    local files
    if (( $# > 0 )); then
        files=( "$@" )
    else
        local orig_nullglob=$(shopt -p nullglob)
        shopt -s nullglob
        files=( * )
        $orig_nullglob
    fi
    if (( "${#files[@]}" > 0 )); then
        stat -c '%s - %n' "${files[@]}"
    fi
}

$ > foo
$ > bar

$ perms foo
0 - foo

$ perms
0 - bar
0 - foo

$ rm foo bar

$ perms
$ echo $?
0

and that will only exit with a failure exit status if one of the commands in the function fails or you pass it an argument for a file name that doesn't exit, which I assume would be desirable behavior:

$ perms nonsense
stat: cannot stat 'nonsense': No such file or directory
$ echo $?
1

The above assumes you're using bash as you tagged. If you're actually using zsh as you also tagged then idk what changes would be needed.