Another way to redirect output in Bash - bash

in bash when we want to read file we use the cat command
cat file.txt
but if we don't want to use whitespace , we can type:
{cat,file.txt}
Is there a way to redirect output without using the symbols > or < or &
i mean is there an equivalent to this command:
cat file.txt > /dev/null
and Thanks.

|tee (called pipe-T) - will redirect output(same as >) to file but also prints at the stdout:
cat file.txt|tee outfile.txt
|tee -a: will append output to file (same as >>) but also prints at the stdout:
cat file.txt|tee -a outfile.txt

I don't understand why you'd want to, but you could do:
eval {cat,input} "$(echo _/dev/null | tr _ '\076')"

Apart from tee You may use exec to redirect the output
exec 3>&1 # making file descriptor 3 to point to 1 where 1 is stdout
exec 1>fileout #redirecting 1 ie stdout to a file
{cat,file} # this goes to fileout & question requirement
exec 1>&3 # restoring 1 to default
{cat,38682813.c} # This will be printed at the stdout

Related

shell: send grep output to stderr and leave stdout intact

i have a program that outputs to stdout (actually it outputs to stderr, but i can easily redirect that to stdout with 2>&1 or the like.
i would like to run grep on the output of the program, and redirect all matches to stderr while leaving the unmatched lines on stdout (alternatively, i'd be happy with getting all lines - not just the unmatched ones - on stdout)
e.g.
$ myprogram() {
cat <<EOF
one line
a line with an error
another line
EOF
}
$ myprogram | greptostderr error >/dev/null
a line with an error
$ myprogram | greptostderr error 2>/dev/null
one line
another line
$
a trivial solution would be:
myprogram | tee logfile
grep error logfile 1>&2
rm logfile
however, i would rather get the matching lines on stderr when they occur, not when the program exits...
eventually, I found this, which gave me a hint to for a a POSIX solution like so:
greptostderr() {
while read LINE; do
echo $LINE
echo $LINE | grep -- "$#" 1>&2
done
}
for whatever reasons, this does not output anything (probably a buffering problem).
a somewhat ugly solution that seems to work goes like this:
greptostderr() {
while read LINE; do
echo $LINE
echo $LINE | grep -- "$#" | tee /dev/stderr >/dev/null
done
}
are there any better ways to implement this?
ideally i'm looking for a POSIX shell solution, but bash is fine as well...
I would use awk instead of grep, which gives you more flexibility in handling both matched and unmatched lines.
myprogram | awk -v p=error '{ print > ($0 ~ p ? "/dev/stderr" : "/dev/stdout")}'
Every line will be printed; the result of $0 ~ p determines whether the line is printed to standard error or standard output. (You may need to adjust the output file names based on your file system.)

Insert a text at the beginning of a file + stdout, stderr

I have function "backup", and output for this function from the console is also redirected to a file, and it's done as follows:
backup > >(tee -a ./log.txt) 2>&1
it works, but I want to add new output data to the beginning of a file, and looks like for my case wisely use ed (not sed), and I doing it in the following way:
ed -s log.txt < <(printf '%s\n' 1i "$(backup)" . wq)
And in this case, I don't know how to implement the output to the console and the file at once (as it happens in my first case). Can somebody give me a hint of implementation?
If I understand your code correctly - you are trying to pre-pend the output of the backup command to the start of the log.txt file. Moreover, you are trying to see the backup output on the console.
Try this:
{
backup 2>&1 | tee /dev/tty
cat log.txt
} > log.txt.new
mv log.txt.new log.txt
Edit:
Here's a version of the code that follows Charles Duffy's suggestion:
cat <( backup 2>&1 | tee /dev/tty ) log.txt > log.txt.new && mv log.txt.new log.txt
Insert a text to the beginning of every line of a file:
sed 's/^/MYTEXT /' path_to_file > path_to_file
Insert a text to the beginning of every stdout line:
COMMAND | sed 's/^/MYTEXT /'

Where a process substitution gets stdin from

In bash I have
grep -vf <(cat myfile <(grep -f myfile otherfile)) otherfile
Given the repetition of myfile, I thought I could pipe it via stdin like so
cat myfile | grep -vf <(cat - <(grep -f - otherfile)) otherfile
However this gives me different results.
So my question is where does the innermost substituted process i.e the grep -f - otherfile, get it's stdin from
A secondary question would be whether there is any advantage to trying to substitute the repeated file name with the same thing passed from stdin
Bash will fork a subshell for process substitution and it will inherit stdin from the current shell.
For your case, the whole right side of | is also running in a subshell so <()'s stdin is the same as this subshell. So <()'s stdin is also from cat myfile.
See the following simpler example:
[STEP 100] # echo $BASH_VERSION
5.0.7(3)-release
[STEP 101] # echo hello | cat <( tr a-z A-Z )
HELLO
[STEP 102] #

bash : parse output of command and store into variable

I have made a command witch return 'version:X' .
ie:
$>./mybox -v
$>version:2
I don't understand why this isn't working :
$>VERSION=$( /home/mybox -v | sed 's/.*version:\([0-9]*\).*/\1/')
$>echo $VERSION
$>
if I write this, it is ok :
$>VERSION=$( echo "version:2" | sed 's/.*version:\([0-9]*\).*/\1/')
$>echo $VERSION
$>2
Regards
It's pretty common for version/error/debugging information to be sent to stderr, not stdout. When running the command from a terminal, both will be printed, but only stdout will make it through the pipe to sed.
echo output always goes to stdout by default, which is why you're not having trouble there.
If the above is correct, you'll just need to redirect stderr (file descriptor 2) to stdout (file descriptor 1) before passing it along:
VERSION=$( /home/mybox -v 2>&1 | sed 's/.*version:\([0-9]*\).*/\1/')
# ^^^^

pipe here document command to log file

Quite often I'll use the following construct to pipe output to a log file, keeping the output also on the display
./command 2>&1 | tee output.log
I'm trying to do something similar, but with using a here document:
./command << HEREDOC
params
HEREDOC 2>&1 | tee output.log
This doesn't work - is it possible to achieve this?
Sure.
./command <<HEREDOC 2>&1 | tee output.log
params
HEREDOC
The here-document doesn't begin until the next line. The rest of the command is parsed as normal.
An example with expr:
xargs expr << HEREDOC | tee output.log
10 + 11
HEREDOC

Resources