0

how can i detect user input is coming from pipe line or with parameters? (as using "if else")

example:

with pipeline

$ cat input_file | ./example.sh
hello
world

with parameters

$ ./example.sh "hello" "world"
hello
world

my false code:

I write url slug shell script. I have a one function for url parsing in script. I use that function for pipe cat a | ./example.sh or user input ./example.sh "hello" "world". My code is correct but I don't understand how detect and check user input is pipe or parameters.

sorry my english

#!/bin/bash

# define replacements
declare -a repls=(
    "Ğg"
    "ğg"
    "Çc"
    "çc"
    "Şs"
    "şs"
    "Üu"
    "üu"
    "Öo"
    "öo"
    "İi"
    "ıi"
    " -"
    "--"
)

function slug() {
    slug=""

    for (( i=0; i<${#arg}; i++ )) 
    do
        char="${arg:$i:1}"
        ascii=$(printf "%d" "'$char")

        # if alphanumeric
        # locale encoding should be UTF-8 for this values to work
        if [[ ( $ascii -ge 48 && $ascii -le 57 ) || # numbers
            ( $ascii -ge 65 && $ascii -le 90 ) ||  # uppercase
            ( $ascii -ge 97 && $ascii -le 122 ) ]]; then # lowercase
            slug="$slug$char"
        else
            for (( j=0; j < ${#repls[@]}; j++ ))
            do
                from=${repls[$j]:0:1}
                to=${repls[$j]:1:1}
                if [[ $char == $from ]]; then
                    slug="$slug$to"
                    break
                fi
            done
        fi
    done

    if [[ $slug == "" ]]; then
        echo "words should contain at least one valid character"
        exit 1
    fi

    echo $slug | awk '{print tolower($0)}'
}

#FOR PARAMETERS
for arg in "$@"
do
 slug;
done

##FOR READ PIPE
[[ -z "$@" ]] && while read arg;
do
 slug;    
done
rojen
  • 184
  • Sorry, I do not understand the question. User input is coming from STDIN (your pipe) if it goes through the loop, and from arguments otherwise. So, you are already using [[ -z "$@" ]] for this test. – Ljm Dullaart May 11 '20 at 10:00
  • Personally, I would make sure that the documentation makes it clear how the program should be invoked (i.e. one way or the other). It's unclear what you want should happen in the case where there are both command line arguments and input on standard input from a pipe. – Kusalananda May 11 '20 at 10:01
  • @LjmDullaart Note that no specific shell was mentioned and that [ -z "$@" ] (in sh) would fail if there are multiple command line arguments. Better to use [ "$#" -eq 0 ]. – Kusalananda May 11 '20 at 10:04
  • @Kusalananda I was merely doing the observation that he already used that test to, in the OP's own words "detect user input is coming from pipe line or with parameters" and, since he already used this test, I did not understand his issue. – Ljm Dullaart May 11 '20 at 10:37
  • I rewrote my code. please look this code and my quenstion? – rojen May 11 '20 at 10:51
  • not a typo. because the string is processed letter by letter. – rojen May 11 '20 at 11:21
  • @Kusalananda not exists for turkish langauge. (specifically written to encoding/decoding URLs). I write a html generator with bash. I dont use more addiction (python, java etc.) – rojen May 11 '20 at 11:25
  • @roaima no I dont used tr, must I try? my script also delete non-ascii chracters – rojen May 11 '20 at 11:28
  • Actually, I don't think tr will work in this situation. Sorry about the misleading there. I'm going to remove my earlier comment, and I'll remove this one shortly too. – Chris Davies May 11 '20 at 11:29
  • I found how make this: if tty -s then echo Terminal else echo Not on a terminal fi – rojen May 11 '20 at 13:01

2 Answers2

1

I'd do:

something_with() {
  printf 'Processing "%s"\n' "$1"
}

ret=0
if [ "$#" -gt 0 ]; then
  # process args on command line

  for arg do
    something_with "$arg" || ret=$?
  done
else
  # no arg, processing lines of stdin instead:

  while IFS= read -r "$arg" || [ -n "$arg" ]; do
    # redirect something_with's stdin to /dev/null to make sure
    # it doesn't interfere with the list of args.
    </dev/null something_with "$arg" || ret=$?
  done
fi

exit "$ret"

(note that it means the arguments sent via stdin cannot contain newline characters).

Though you could also only take input as arguments, but invoke your script as:

xargs -rd '\n' -a input_file your-script

(here assuming GNU xargs), for xargs to pass the contents of the lines in input_file as arguments to your-script (in that case your-script may be called several time by xargs to work around the limit on the maximum number of arguments to a command).

In any case, I'd say you don't want to check whether stdin is a pipe or not here.

  1. First that cat input_file | your-script is a Useless Use of Cat (infamous UUoC). Generally, if you want to feed the contents of a file as input to command, you use < input_file your-scrip or your-script < input_file, in which case your script's stdin will not be a pipe (unless input_file itself is a named pipe).

  2. Your script could be called with stdin connected with a pipe even though you don't want it to read it, like for instance in ssh host your-script arg1 arg2 (where stdin a a pipe to sshd) or ... | while IFS= read -r foo; do your-script "x$foo"; done or cmd | xargs your-script (with some xargs implementation; some redirect stdin to /dev/null instead there).

But if you really wanted to, that's already addressed in a separate question on this site: How does a program know if stdout is connected to a terminal or a pipe? with the different that here it's stdin instead of stdout, so file descriptor 0 instead of 1.

-2

You may check if your shell is interactive via the $- variable or via the $PS1 variable:

case "$-" in
*i*)    echo This shell is interactive ;;
*)  echo This shell is not interactive ;;
esac

or

if [ -z "$PS1" ]; then
        echo This shell is not interactive
else
        echo This shell is interactive
fi

The above examples are copied from here.

Thanks to @Kusalananda: one further option to check if parameters are piped into the script:

if [ -t 0 ]; then
  echo "This shell is interactive"
fi
noAnton
  • 361
  • 2
    Also, [ -t 0 ] will be true if standard input is open and connected to a terminal. – Kusalananda May 11 '20 at 10:24
  • thank you for pointing this out - I will add this to the answer – noAnton May 11 '20 at 10:59
  • 1
    How does looking at $- help? It doesn't contain i regardless of if the script was started with ./foo.sh asdf, echo asdf | ./foo.sh, or bash < ./foo.sh. An interactive shell is where the shell commands are read from a terminal (roughly), but here they come from the script file. Sure, you could run bash -i ./foo.sh, but that's not really very user-friendly to require. – ilkkachu May 11 '20 at 11:43