I'm trying to use the system command to output text to a file. I'm using echo -n and passing -n so it doesn't automatically create a newline in the file. Here's the command I'm using:
system("echo -n \"text for the file\" >> path/to/file.txt")
When I check the text file, the output that I get is:
-n text for file
If I run that same exact command in my terminal, I don't get the -n
Why is it not accepting the -n as an argument?
echo -n is not portable. Use printf instead to print the string.
system("printf 'text of the file' >> path/to/file.txt")
Specifically, the POSIX specification allows a conforming implementation of echo to support a -n option, but does not require it or define what it should do. Your system call is using a shell (probably dash) that does not implement -n and treats it as a regular argument to write to standard output.
Alongside, chepner's useful answer, since the system() call calling the native implementation of echo which does not implement the -n functionality, you could call the FreeBSD or a GNU based installation of echo installed explicitly as below,
In Mac OS the FreeBSD version of echo is installed as /bin/echo which you can use as
system("/bin/echo -n \"text for the file\" >> path/to/file.txt")
As a side-note, you could first see if echo is installed by doing which echo before running this.
Related
Take the following example:
ls -l | grep -i readme | ./myscript.sh
What I am trying to do is get ls -l | grep -i readme as a string variable in myscript.sh. So essentially I am trying to get the whole command before the last pipe to use inside myscript.sh.
Is this possible?
No, it's not possible.
At the OS level, pipelines are implemented with the mkfifo(), dup2(), fork() and execve() syscalls. This doesn't provide a way to tell a program what the commands connected to its stdin are. Indeed, there's not guaranteed to be a string representing a pipeline of programs being used to generate stdin at all, even if your stdin really is a FIFO connected to another program's stdout; it could be that that pipeline was generated by programs calling execve() and friends directly.
The best available workaround is to invert your process flow.
It's not what you asked for, but it's what you can get.
#!/usr/bin/env bash
printf -v cmd_str '%q ' "$#" # generate a shell command representing our arguments
while IFS= read -r line; do
printf 'Output from %s: %s\n' "$cmd_str" "$line"
done < <("$#") # actually run those arguments as a command, and read from it
...and then have your script start the things it reads input from, rather than receiving them on stdin.
...thereafter, ./yourscript ls -l, or ./yourscript sh -c 'ls -l | grep -i readme'. (Of course, never use this except as an example; see ParsingLs).
It can't be done generally, but using the history command in bash it can maybe sort of be done, provided certain conditions are met:
history has to be turned on.
Only one shell has been running, or accepting new commands, (or failing that, running myscript.sh), since the start of myscript.sh.
Since command lines with leading spaces are, by default, not saved to the history, the invoking command for myscript.sh must have no leading spaces; or that default must be changed -- see Get bash history to remember only the commands run with space prefixed.
The invoking command needs to end with a &, because without it the new command line wouldn't be added to the history until after myscript.sh was completed.
The script needs to be a bash script, (it won't work with /bin/dash), and the calling shell needs a little prep work. Sometime before the script is run first do:
shopt -s histappend
PROMPT_COMMAND="history -a; history -n"
...this makes the bash history heritable. (Code swiped from unutbu's answer to a related question.)
Then myscript.sh might go:
#!/bin/bash
history -w
printf 'calling command was: %s\n' \
"$(history | rev |
grep "$0" ~/.bash_history | tail -1)"
Test run:
echo googa | ./myscript.sh &
Output, (minus the "&" associated cruft):
calling command was: echo googa | ./myscript.sh &
The cruft can be halved by changing "&" to "& fg", but the resulting output won't include the "fg" suffix.
I think you should pass it as one string parameter like this
./myscript.sh "$(ls -l | grep -i readme)"
I think that it is possible, have a look at this example:
#!/bin/bash
result=""
while read line; do
result=$result"${line}"
done
echo $result
Now run this script using a pipe, for example:
ls -l /etc | ./script.sh
I hope that will be helpful for you :)
I have a file with a list of servers:
SERVERS.TXT:
192.168.0.100
192.168.0.101
192.168.0.102
From a gnome terminal script, I want open a new terminal, with a tab for each server.
Here is what I tried:
gnome-terminal --profile=TabProfile `while read SERVER ; do echo "--tab -e 'ssh usr#$SERVER'"; done < SERVERS.TXT`
Here is the error:
Failed to parse arguments: Argument to "--command/-e" is not a valid command: Text ended before matching quote was found for '. (The text was ''ssh')
Tried removing the space after the -e
gnome-terminal --profile=TabProfile `while read SERVER ; do echo "--tab -e'ssh usr#$SERVER'"; done < SERVERS.TXT`
And I get a similar error:
Failed to parse arguments: Argument to "--command/-e" is not a valid command: Text ended before matching quote was found for '. (The text was 'usr#192.168.0.100'')
Obviously there is a parsing error since the the shell is trying to be helpful by using the spaces to predict and place delimiters. The server file is changed without notice and many different sets of servers need to be looked at.
I found this question while searching for an answer to the issue the OP had, but my issue was a little different. I knew the list of servers, they where not in a file.
Anyway, the other solutions posted did not work for me, but the following script does work, and is what I use to get around the "--command/-e" is not a valid command" error.
The script should be very easy change to suit any need:
#!/bin/sh
# Open a terminal to each of the servers
#
# The list of servers
LIST="server1.info server2.info server3.info server4.info"
cmdssh=`which ssh`
for s in $LIST
do
title=`echo -n "${s}" | sed 's/^\(.\)/\U\1/'`
args="${args} --tab --title=\"$title\" --command=\"${cmdssh} ${s}.com\""
done
tmpfile=`mktemp`
echo "gnome-terminal${args}" > $tmpfile
chmod 744 $tmpfile
. $tmpfile
rm $tmpfile
Now the big question is why does this work when run from a file, but not from within a script. Sure, the issue is about the escaping of the --command part, but everything I tried failed unless exported to a temp file.
I would try something like:
$ while read SERVER;do echo -n "--tab -e 'ssh usr#$SERVER' "; \
done < SERVERS.txt | xargs gnome-terminal --profile=TabProfile
This is to avoid any interpretation that the shell could do of the parameters (anything starting with a dash).
Because it is concatenating strings (using -n), it is necessary to add an space between them.
Is this a problem of parsing command-line options? Sometimes if you have one command sending arguments to another command, the first can get confused. The convention is to use a -- like so:
echo -- "--tab -e 'ssh usr#$SERVER'";
Try to type
eval
before gnome terminal command.
it should be something like this:
eval /usr/bin/gnome-terminal $xargs
worked for me!
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.
Since Solaris grep by default doesn't have the -E option, I would have to update my bash to work with a specific grep. This is what I do.
It works on the command line, but when I put it in the bash file, it looks like the script doesn't pick it up and still uses the normal grep. (I do not want to change the whole $PATH.)
Please advise:
export isSolaris=`uname -a | grep -i "sunos"`
if [ -n "$isSolaris" ]; then
alias grep="/usr/xpg4/bin/grep -E";
fi
bash and ksh don't process aliases defined in a file until after the file is read... which means you can't define it in the same script that will use it. You can put it in another file and . (source) that into your script, though.
Alternately, use a shell function.
mygrep() {
if test -n "$isSolaris"; then
/usr/xpg4/bin/grep -E ${1+"$#"}
else
grep ${1+"$#"}
fi
}
Consider using egrep instead; it probably works everywhere, even though POSIX/SUS doesn't list it as a command any more. (SUS v2 from 1997 listed egrep as a 'legacy' utility; POSIX 1003.1:2004 omitted egrep).
I'm using GNU bash, version 3.00.15(1)-release (x86_64-redhat-linux-gnu). And this command:
echo "-e"
doesn't print anything. I guess this is because "-e" is one of a valid options of echo command because echo "-n" and echo "-E" (the other two options) also produce empty strings.
The question is how to escape the sequence "-e" for echo to get the natural output ("-e").
The one true way to print any arbitrary string:
printf "%s" "$vars"
This is a tough one ;)
Usually you would use double dashes to tell the command that it should stop interpreting options, but echo will only output those:
$ echo -- -e
-- -e
You can use -e itself to get around the problem:
$ echo -e '\055e'
-e
Also, as others have pointed out, if you don't insist on using the bash builtin echo, your /bin/echo binary might be the GNU version of the tool (check the man page) and thus understand the POSIXLY_CORRECT environment variable:
$ POSIXLY_CORRECT=1 /bin/echo -e
-e
There may be a better way, but this works:
printf -- "-e\n"
You could cheat by doing
echo "-e "
That would be dash, e, space.
Alternatively you can use the more complex, but more precise:
echo -e \\\\x2De
[root#scintia mail]# POSIXLY_CORRECT=1; export POSIXLY_CORRECT
[root#scintia mail]# /bin/echo "-e"
-e
[root#scintia mail]#
Another alternative:
echo x-e | sed 's/^x//'
This is the way recommended by the autoconf manual:
[...] It is often possible to avoid this problem using 'echo "x$word"', taking the 'x' into account later in the pipe.
After paying careful attention to the man page :)
SYSV3=1 /usr/bin/echo -e
works, on Solaris at least
I like that one using a herestring:
cat <<<"-e"
Another way:
echo -e' '
echo -e " \b-e"
/bin/echo -e
works, but why?
[resin#nevada ~]$ which echo
/bin/echo