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
10
  • 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. Commented May 11, 2020 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
    Commented May 11, 2020 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
    Commented May 11, 2020 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. Commented May 11, 2020 at 10:37
  • I rewrote my code. please look this code and my quenstion?
    – rojen
    Commented May 11, 2020 at 10:51

2 Answers 2

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.

10
  • I rewrote my code. please look this code and my quenstion?
    – rojen
    Commented May 11, 2020 at 10:52
  • the code you suggested works correctly but I want pipe available
    – rojen
    Commented May 11, 2020 at 11:16
  • @rojen, that code will read stdin whether it's a pipe or not as long as you don't pass any argument to the script. Commented May 11, 2020 at 11:22
  • i will try the suggestion. thanks for helping, @Stephane. and than I am coming
    – rojen
    Commented May 11, 2020 at 11:32
  • I found how make this: if tty -s then echo Terminal else echo Not on a terminal fi
    – rojen
    Commented May 11, 2020 at 13:03
-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
3
  • 2
    Also, [ -t 0 ] will be true if standard input is open and connected to a terminal.
    – Kusalananda
    Commented May 11, 2020 at 10:24
  • thank you for pointing this out - I will add this to the answer
    – noAnton
    Commented May 11, 2020 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
    Commented May 11, 2020 at 11:43

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .