How do I use the file redirected to the standard input of a bash script? - bash

How do I use the file redirected to the standard input of a bash script?
$ script.sh < file_to_use_in_script
what do I have to put in my script, so that I can write the filename in a variable from this input, without knowing the pathname beforehand.
FILENAME=$file_to_use_in_script

You can use the filename /dev/stdin. Make sure to only read it once.
$ cat myscript
#!/bin/bash
file=${1:-/dev/stdin}
echo "Reading from $file"
nl "$file"
$ cat myfile
hello world
$ ./myscript myfile
Reading from myfile
1 hello world
$ ./myscript < myfile
Reading from /dev/stdin
1 hello world
$ echo "something" | ./myscript
Reading from /dev/stdin
1 something

i would suggest to pass the filename as a an argument, you obviously know it anyway
echo "name $1"
while read line
do
echo $line
done
and then:
./test.sh foo/bar.txt < foo/bar.txt
gives
name foo/bar.txt
1
2
3
if foo/bar.txt contains
1
2
3

Related

Is there a way to print interpolated shell commands while preserving redirections?

Suppose I have the following shell program.
#!/bin/sh
FOO="foo"
echo $FOO | cat
I want to generate another shell program that does the same thing as this one, except that all shell variables have been substituted. For example,
#!/bin/sh
echo "foo" | cat
I know that I can get close if I run the above program using #!/bin/sh -x, but that output does not preserve redirections. Instead, I get
+ FOO=foo
+ echo foo
+ cat
foo
Any ideas?
The following shell:
$ cat eval.sh
echo "#!/bin/sh"
FOO="foo"
echo "echo $FOO | cat"
will write a shell:
$ sh eval.sh
#!/bin/sh
echo foo | cat
which does what you need.

Testing whether stdin is a file vs. a pipe vs. a tty

I know for bash and zsh, one can use e.g. [ -t 1 ] to determine if STDIN is an interactive tty session.
However, there doesn't seem to be a way to test whether stdin is being redirected from a file, versus being piped in from a command:
foo < ./file
bar | foo
Is there any way to detect the difference between these two? Separately, is there any way to get the path of the file being redirected from (outside of /proc/self, which is unavailable on macOS)?
You can check if /dev/stdin is a regular file or a pipe:
$ cat tmp.sh
#!/bin/bash
if [ -f /dev/stdin ]; then
echo "file"
elif [ -p /dev/stdin ]; then
echo "pipe"
fi
$ bash tmp.sh < foo.txt
file
$ echo foo | bash tmp.sh
pipe
This relies on /dev/stdin being in your file system, though.
You can also use the stat command, which will return information about standard input given no file name argument. As you mentioned you are using macOS, you can use the %HT format:
$ stat -f %HT
Character Device
$ stat -f %HT < foo.txt
Regular File
$ echo foo | stat -f %HT
Fifo File

From within a shell script I want to access input given from other command

I am messing around with shell scripts and I am trying to get my script to take input from another command, like say ls. It is called like this:
ls | ./example.sh
I try to access the input from within example.sh
#!/bin/bash
echo $1
but it echoes back nothing. Is there another way to reference parameters given to bash by other commands, because it works if I type in:
./example.sh poo
Parameters and input aren't the same thing:
$ ls
example.sh foo
$
$ cat example.sh
for param; do
printf 'argument 1: "%s"\n' "$param"
done
while IFS= read -t 1 -r input; do
printf 'input line: "%s"\n' "$input"
done
$
$ ls | ./example.sh
input line: "example.sh"
input line: "foo"
$
$ ls | ./example.sh bar
argument 1: "bar"
input line: "example.sh"
input line: "foo"
$
$ ./example.sh $(ls)
argument 1: "example.sh"
argument 1: "foo"
Parameters are the arguments passed to the script to start it running, input is what the script reads while it's running.
You can use xargs to do that.
ls | xargs ./example.sh
Resource
https://ss64.com/bash/xargs.html
https://www.cyberciti.biz/faq/linux-unix-bsd-xargs-construct-argument-lists-utility/
Or read man page for it

How to label std output in a bash script [duplicate]

This question already has an answer here:
List content of one or more files with a header showing the file name
(1 answer)
Closed 7 years ago.
I have a basic script that looks like this:
#!/bin/bash
### run these 2 commands and dump the contents to a file
run command 1 > /dir/file1
run command 2 > /dir/file2
##### run this command, echo the output and email me if $var is above a certain # and send the contents of the dump files in the email
var=$(netstat -an | wc -l)
echo $var
if [ "$var" -ge 700 ]; then
cat /dir/file1 /dir/file2 | mailx -s "System X has over $var connections. " email recipients
fi
What I need to do is label the contents to distinguish between the 2 files so when I get the email I know what output came from command 1 and what came from command 2. What is the best way to do this?
You can abuse head:
$ cat file1
Contents of file 1
$ cat file2
Contents of file 2
$ head -n -0 file*
==> file1 <==
Contents of file 1
==> file2 <==
Contents of file 2
Try:
{ echo "From command 1:"; cat /dir/file1; echo "From command 2:"; cat /dir/file2; } | mailx -s "System X has over $var connections." email recipients

Redirect stdin in a script to another process

Say I have a bash script that get some input via stdin. Now in that script I want to launch another process and have that process get the same data via its stdin.
#!/bin/bash
echo STDIN | somecommand
Now the "echo STDIN" thing above is obviously bogus, the question is how to do that? I could use read to read each line from stdin, append it into a temp file, then
cat my_temp_file | somecommand
but that is somehow kludgy.
When you write a bash script, the standard input is automatically inherited by any command within it that tries to read it, so, for example, if you have a script myscript.sh containing:
#!/bin/bash
echo "this is my cat"
cat
echo "I'm done catting"
And you type:
$ myscript.sh < myfile
You obtain:
this is my cat
<... contents of my file...>
I'm done catting
Can tee help you?
echo 123 | (tee >( sed s/1/a/ ) >(sed s/3/c/) >/dev/null )

Resources