71

Suppose I have a bash shell script called Myscript.sh that need one argument as input.

But I want the content of the text file called text.txt to be that argument.

I have tried this but it does not work:

cat text.txt | ./Myscript.sh

Is there a way to do this?

9 Answers 9

49

Command substitution.

./Myscript.sh "$(cat text.txt)"
1
  • 2
    This works, but if cat fails, then Myscript.sh will still execute. Piping with xargs ensures that the execution stops. May 7, 2020 at 14:07
68

You can use pipe output as a shell script argument.

Try this method:

cat text.txt | xargs -I {} ./Myscript.sh {}
2
  • 2
    Can someone please explain how this command work? Feb 11, 2022 at 6:40
  • 1
    @TalhaJunaid, after some reading and a quick experimentation I can tell that -I {} (placeholder) splits the piped output (reading that text.txt with cat) - into as many arguments as there are lines in that file; so if there are 3 lines, then Myscript.sh will be executed 3 times: one for every line (substituting {} argument placeholder)
    – retif
    Mar 27, 2022 at 20:33
6

To complete @bac0n which IMHO is the only one to correctly answers the question, here is a short-liner which will prepend piped arguments to your script arguments list :

#!/bin/bash

declare -a A=("$@")
[[ -p /dev/stdin ]] && { \
    mapfile -t -O ${#A[@]} A; set -- "${A[@]}"; \
}

echo "$@"

Example use :

$ ./script.sh arg1 arg2 arg3
> arg1 arg2 arg3

$ echo "piped1 piped2 piped3" | ./script.sh
> piped1 piped2 piped3

$ echo "piped1 piped2 piped3" | ./script.sh arg1 arg2 arg3
> piped1 piped2 piped3 arg1 arg2 arg3

1
  • mapfile reads lines from stdin as default if you want to change this behavior, e.g., if you like to split on words, you have to specify a delimiter.
    – user1105628
    Dec 24, 2020 at 12:22
5

Pipe it into xargs before the actual command, like cat text.txt | xargs ./Myscript.sh

4

If you have more than one set of arguments in the file (for multiple calls), consider using xargs or parallel, e.g.

xargs -d '\n' Myscript.sh < text.txt
parallel -j4 Myscript.sh < text.txt
2

By reading stdin with mapfile you can re-set the positional parameters.

#!/bin/bash

[[ -p /dev/stdin ]] && { mapfile -t; set -- "${MAPFILE[@]}"; }

for i in "$@"; do
    echo "$((++n)) $i"
done

$ cat test.txt | ./script.sh
1 first
2 second line 
3 third
0

Try,

 $ cat comli.txt
 date
 who
 screen
 wget

 $ cat comli.sh
 #!/bin/bash
 which $1

 $ for i in `cat comli.txt` ; do ./comli.sh $i ; done

so you can enter the values one by one to comli.sh from comli.txt.

0

If there are newlines (LF) or tabs in the file and you want to actually put them in one single argument (not have bash convert LF to spaces and use each word as a separate argument), you can use this:

./Myscript.sh "$(IFS=''; cat text.txt)"

(subshell used so IFS is untouched in main shell)

Simple test to show it works:

echo ">>>$(IFS=''; cat text.txt)<<<"

Non-sub-shell version:

IFS=" " ; echo ">>>$(cat text.txt)<<<" ; IFS=$' \t\n'

See also: https://stackoverflow.com/questions/2789319/file-content-into-unix-variable-with-newlines

0

This q's a bit old, but I found this solution easiest to understand and wanted to share.

If you want to accept the contents of test.txt as input to your script (e.g. cat test.txt | ./myEchoScript), you can do the following to read from stdin

if [ -p /dev/stdin ]; then
        input=$(cat)
else
        echo "expecting input to be piped in, exiting script"
        exit
fi
echo $input

Then to run:

$ echo "testing testing" > test.txt
$ cat test.txt | ./myEchoScript.sh 
testing testing

You must log in to answer this question.

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