Shell scripting: cat vs echo for output - bash

I've written a small script in bash that parses either the provided files or stdin if no file is given to produce some output. What is the best way to redirect the parsed output to stdout (at the end of the script the result is stored in a variable). Should I use cat or echo, or is there another preferred method?

Use the printf command:
printf '%s\n' "$var"
echo is ok for simple cases, but it can behave oddly for certain arguments. For example, echo has a -n option that tells it not to print a newline. If $var happens to be -n, then
echo "$var"
won't print anything. And there are a number of different versions of echo (either built into various shells or as /bin/echo) with subtly different behaviors.

echo. You have your parsed data in a variable, so just echo "$var" should be fine. cat is used to print the contents of files, which isn't what you want here.

echo is a fine way to do it. You will have to jump through a few hoops if you want cat to work.

Related

Concatenate variables to a String bash script

I am trying to Concatenate variables with Strings in my bash script, the variables are being read independently but whenever I try to concatenate them, it doesn't recognize the variable values.
ex-
echo $CONFIG_PROTOCOL (Prints the variable value, HTTP)
echo $CONFIG_PROTOCOL'://'$CONFIG_SERVER_SOURCE:$CONFIG_PORT'/api/creation/objects/export?collection.ids='$sf
The above echo with the URL prints out /api/creations/objects/export?collection.ids=value_1, while it should print out http://localhost:8080/api/creation/objects/export?collection.ids=value_1
Any inputs will be appreciated.
This happens because $CONFIG_PORT has a trailing carriage return. Here's a MCVE:
CONFIG_PROTOCOL="http"
CONFIG_SERVER_SOURCE="example.com"
CONFIG_PORT="8080"
sf="42"
# Introduce bug:
CONFIG_PORT+=$'\r'
echo $CONFIG_PROTOCOL'://'$CONFIG_SERVER_SOURCE:$CONFIG_PORT'/api/creation/objects/export?collection.ids='$sf
When executed, this prints:
/api/creation/objects/export?collection.ids=42
When you comment out the buggy line, you get:
http://example.com:8080/api/creation/objects/export?collection.ids=42
In both cases, echo will appear to show the correct values because echo is a tool for showing text to humans and not useful for showing the underlying data. printf '%q\n' "$CONFIG_PORT" will instead show it in an unambiguous format:
$ echo $CONFIG_PORT
8080
$ printf '%q\n' "$CONFIG_PORT"
$'8080\r'
The best way to fix this is to ensure that whatever supplies the value does so correctly. But the easiest way is to just strip them:
echo $CONFIG_PROTOCOL'://'$CONFIG_SERVER_SOURCE:$CONFIG_PORT'/api/creation/objects/export?collection.ids='$sf | tr -d '\r'
echo "$CONFIG_PROTOCOL://$CONFIG_SERVER_SOURCE:$CONFIG_PORT/api/creation/objects/export?collection.ids=$sf"
Try above echo statement

How to pass shell variables in "echo" command

I have she script as the below content
chr=$0
start=$1
end=$2
echo -e "$chr\t$start\t$end" > covdb_input.bed
How do i pass the chr,Start and end variables in to echo command.. or write same to file "covdb_input.bed" with TAB sep as in echo command.
You're doing everything right, except that you probably initialize your variables with the wrong things.
I'm assuming you get arguments for the script (or shell function), and that you want to use these. Then pick the positional variables from $1 and onwards as $0 will usually contain the name of the current shell script or shell function.
Also, you might find people scoffing about the use of -e with echo (it's a common but non-standard option). Instead of using echo you could use printf like this:
printf "%s\t%s\t%s" "$chr" "$start" "$end" >myfile.bed
Or just
printf "$chr\t$start\t$end" >myfile.bed

How do I tell if a variable is a file in bash

I am not a great bash scripter and hence have a few questions. One of which is how (or even whether) bash understands that a variable is a "file" or simply a local variable.
file=/usr/share/lib
Obviously this is a file to be saved, etc and can be used like so:
echo "$output" > $file
To save the output of $output to $file.
But where in bash does it calculate whether it's a file or not, is it only a file once it's been passed to a 'writing method'?
If you treat that variable as a file name, bash will simply do what it's told e.g.
echo "Test output" > $file
will work regardless of file being set to /tmp/myfile.txt, or to abcd. In the above you're using bash's file redirection to write out the standard out to the file you've named.
Consequently if you use the wrong variable in the above pattern, or have the value set incorrectly, bash will simply follow your instructions and you may end up with incorrectly named/located files.
You need to check this yourself, bash is only aware of the contents of the variable. If you want to check if a file location is held within a variable, you can test for it using the -f test operator
if [ -f "$file" ]
then
echo "$file is a file"
echo "$output" > "$file"
else
echo "$file is not a file"
fi
Variables are strings - nothing more, nothing less. (I'm ignoring array variables here, WLOG).
Bash (or any shell) expands variables blindly, without considering what you intend to do with them. It's only in the next stage of command processing that the contents of the string matter.
To use your example:
output="foo bar"
file=/usr/share/lib
echo "$output" >"$file"
(I've quoted $file even though it's not necessary here, simply because I've been bitten too many times by changing the value and breaking everything).
The line
echo "$output" >"$file"
gets transformed into
echo "foo bar" >"/usr/share/lib"
and only then does bash consider the > and attempt to open /usr/share/lib for writing.

Shell script echo outputting arguments

I'm currently trying to build a shell script that sends broadcast UDP packets. My problem is that my echo is outputting the arguments instead, and I have no ideia why. Here's my script:
#!/bin/bash
# Script
var1="\xdd\x02\x00\x13\x00\x00\x00\x10\x46\x44\x30\x30\x37\x33\x45\x31\x39\x39\x45\x43\x31\x42\x39\x34\x00"
var2="\xdd\x00\x0a\x\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x02"
echo -ne $var1 | socat - UDP4-DATAGRAM:255.255.255.255:5050,broadcast
echo -ne $var2 | socat - UDP4-DATAGRAM:255.255.255.255:5050,broadcast
Using wireshark I can see the script is printing -ne as characters and also is not converting each \xHH to the correspondant ASCII character.
Thanks!
I figured my problem out. It turns out I was runninc my script with sh ./script.sh instead of bash ./script.sh
echo implementations are hopelessly inconsistent about whether they take command options (like -ne) or simply treat them as part of the string to print, and/or whether they interpret escape sequences in the strings to print. It sounds like you're seeing a difference between bash's builtin version of echo vs. (I'm guessing) the version in /bin/echo. I've also seen it vary even between different versions of bash.
If you want consistent behavior for anything nontrivial, use printf instead of echo. It's slightly more complicated to use it correctly, but IMO worth it because your scripts won't randomly break because echo changed for whatever reason. The tricky thing about printf is that the first argument is special -- it's a format string in which all escape sequences are interpreted, and any % sequences tell it how to add in the rest of the arguments. Also, it doesn't add a linefeed at the end unless you specifically tell it to. In this case, you can just give it the hex codes in the format string:
printf "$var1" | socat - UDP4-DATAGRAM:255.255.255.255:5050,broadcast
printf "$var2" | socat - UDP4-DATAGRAM:255.255.255.255:5050,broadcast

Significance of echo Start|cat>>log in bash

Can anyone help me out with the following code snippet -
echo Start|cat>>log
When I tried
echo Start>>log
it gave the same output to the log file. Can anyone explain the difference between the two commands?
cat is one of those programs that can take an argument and use it, or just use its standard input if you don't provide an argument. In other words, while:
cat xyzzy
will open the file xyzzy and output its contents, the command:
cat
on its own will read its standard input and send that to standard output.
Hence piping some output through cat without an argument is no different that just sending the output without cat, other than creating a superfluous process. In other words, these two are functionally identical:
echo xyzzy | cat
echo xyzzy
You can use either but the latter (for both my example above and in your question) will use one less process and a few less keystrokes. The cat filter on its own will simply pass the data through as-is and hence is not necessary.

Resources