7

I have a number of shell functions that I use in my command line environment and in my shell scripts. I want two of them, upper and lower, to behave differently depending on whether or not data is being piped to them. Both functions are currently written such that they receive input through arguments passed to them.

This is the definition of upper:

upper () {
    my_echo "${1}" | tr "[:lower:]" "[:upper:]"
    return 0
}

This is the definition of lower:

lower () {
    my_echo "${1}" | tr "[:upper:]" "[:lower:]"
    return 0
}

(my_echo is a Python script I wrote as a replacement for echo. It's supposed to output whatever gets passed to it as an argument while converting escape sequences into their literals. I'd be able to print any string without it being interpreted as an option, like -e for echo. The way Unix consoles work threw a wrench into that goal, so I shelved it, so you can just ignore it.)

There are several past incarnations of these functions. These are the definitions of the versions that operate on standard input:

upper () {
    tr "[:lower:]" "[:upper:]"
}

lower () {
    tr "[:upper:]" "[:lower:]"
}

If the function being piped to, then it should be operate like the above. If it it isn't, it should read the first parameter.

  • 4
    have you tried test -t? e.g. [ ! -t 0 ] && echo stdin is not a terminal - compare with echo foo | [ ! -t 0 ] && echo stdin is not a terminal. i can't remember right now if this is bash-specific or common to most or all sh variants....just tested in ksh and dash, works in them same as bash, but that might be because I have GNU coreutils installed, which provides test aka [. – cas Jul 24 '17 at 3:55
  • echo - -e prints - -e – cat Jul 24 '17 at 4:34
1

Your code:

upper () {
    my_echo "${1}" | tr "[:lower:]" "[:upper:]"
    return 0
}

To make this read from standard input if there is no arguments:

upper () {
    ( (( $# )) && printf '%s\n' "$@" || cat ) |
    tr '[:lower:]' '[:upper:]'
}

This tests against $#, the number of positional parameters, and if that's zero it uses cat reading from the standard input stream, otherwise it uses printf on the positional arguments. This is then piped to tr.

I removed return 0 since you're likely to want to know the real exit status of the pipeline.

In bash, you may do

upper () {
    (( $# )) && printf '%s\n' "${@^^}" || tr '[:lower:]' '[:upper:]'
}

When there are arguments, this will not use tr at all but Bash's ${parameter^^pattern} substitution to turn all arguments into uppercase (use ,, to lowercase).

You may or may not want to change the printf format string so that it's '%s ' rather than '%s\n' depending on what you want to achieve.

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.