1

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"
J. Scott Elblein
  • 4,013
  • 15
  • 58
  • 94
  • 2
    Use a function and enjoy. – Jetchisel Mar 17 '23 at 21:51
  • 1
    Tag bash **or** zsh, not both at once. They're deeply incompatible shells, with design philosophies that force them apart further over time (bash tries to stay a superset of POSIX sh, zsh deliberately breaks the standard in places where it disagrees with standardized decisions). – Charles Duffy Mar 17 '23 at 21:53
  • 2
    (Also, `a && b || c` is not a ternary operator, and trying to use it as if it were one is liable to end in tears). – Charles Duffy Mar 17 '23 at 21:57
  • "Deeply incompatible" is an exaggeration; you make it sound like `fish`. `zsh` can (or can be configured to) run most `bash` code just as `bash` would. `zsh` makes no secret about its purpose as an enhanced *interactive* shell, not a replacement for executing shell scripts. If you really want it to be, you can configure `zsh` to be POSIX-compliant. – chepner Mar 17 '23 at 21:57
  • @chepner, sure -- when configured in POSIX mode it loses everything that makes it unique, though. I do stand behind my description of the failure to perform POSIX-mandated word-splitting an unquoted expansion as utterly fundamental; that operation is so ubiquitous that any nontrivial code written to take advantage of zsh's laxity will be buggy when run on POSIX-compliant shells. Certainly, one can write code that works for both -- you've done it with your answer -- but folks need to be mindful; "eh, works on zsh, so this should be fine for everyone" is Not Okay. – Charles Duffy Mar 17 '23 at 21:58
  • (I'm a lot happier with fish: it's _obviously_ not a POSIX shell, to a degree that makes it harder for finger memory / habits to inadvertently transfer) – Charles Duffy Mar 17 '23 at 22:03
  • 1
    Why is this question tagged with `bash` and `zsh`? – Cyrus Mar 17 '23 at 22:04
  • "liable to end in tears" lmao. Trust me, it did end in tears, lol. Dealing w/Linux when you've been a Windows guy your whole life usually does. Kinda glad I did tag both though; it's been an educational discussion for me. ;) – J. Scott Elblein Mar 18 '23 at 00:44
  • @EdMorton-SOstopbullying I thought that was a very odd "ternary" as well, using a `&&` and `||`, but came across it in searches, including here on SO, so gave it a shot. Bummer though that there are no ternary's in bash ... I tend to find ternarys ded sexeh. :p – J. Scott Elblein Mar 18 '23 at 00:53
  • @J.ScottElblein then you'll be very disappointer if you ever have to use [Go](https://go.dev/) (aka `Golang`) as the providers think the rest of us are too stupid to be trusted with ternaries:-). I agree with you FWIW - they're extremely useful when used sensibly. – Ed Morton Mar 18 '23 at 00:58
  • I *have* been wanting to skinny dip in the Golang waters as well lately. Been debating on either Golang, Python, or JS. There are NO ternarys in Go?! *clutches invisible pearls* – J. Scott Elblein Mar 18 '23 at 01:02
  • 2
    Right, no ternaries in Go (google it for the providers' highly debatable rationale which really does boil down to "everyone's too stupid to use them properly"). – Ed Morton Mar 18 '23 at 01:04
  • Technically, `bash` *does* have a ternary operator.... if you are in an arithmetic context. `x=$(( y < 0 ? -1 : y > 0 ? 1 : 0))`, for example, works as a quasi-`sign` function. – chepner Mar 19 '23 at 16:48
  • Aside from what chepner mentioned: `$(x ....)` runs the command `x`, where `x` is an external command, or a function, or an alias. `if` is neither of those, unless you happen to have an executable program named _if_ somewhere in your PATH. Same is true for `[[`. – user1934428 Mar 20 '23 at 13:27

2 Answers2

2

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
}
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
chepner
  • 497,756
  • 71
  • 530
  • 681
2

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.

Ed Morton
  • 188,023
  • 17
  • 78
  • 185