I want to use some output of a command, but I don't know, how I can save the output in a variable or file.
The script is connecting to a specified server via telnet and executes different commands. One command is needed for further commands. Well... I have to save these informations from the command output.
My code:
#!/bin/bash
(
echo open server portnumber
sleep 2s
echo "login username password"
sleep 1s
echo "use sid=1"
CLIENT_LIST=$(echo "clientlist")
sleep 1s
echo "clientupdate client_nickname=Administrator"
for client_id in $(echo $CLIENT_LIST | grep -Eo "clid=[0-9]+" | grep -Eo "[0-9]+"); do
echo "clientpoke clid=$client_id msg=How\sare\syou!"
sleep 1s
done
sleep 1s
echo "logout"
echo "quit"
) | telnet
I want to save the output of the command 'clientlist' in a variable or file. A variable would be the best solution. But actually the variable just saves 'clientlist' and not the output of the command. :(
I hope somebody can help me. Thanks in advance! :)
If you want to test it: It's made for TeamSpeak 3 server.
To run the command 'clientlist' and save the output in a variable:
output_var=$(clientlist)
In bash or sh, the $(...) syntax means "run this command and return whatever output it produces."
If by this question you mean "I want to run the clientlist command on the remote machine, then capture its output to a variable on the local machine", then you can't do this using something like your script. (Note that you script only pipes input into the telnet command; telnet's output isn't captured anywhere.)
You'll need to write a script using something like expect, or one of its work-alikes in another language like Perl or Python.
Related
I'm trying to capture the output of an scp command to a variable or array in Bash.
I'm not exactly sure where the text comes from local or remote ?
So far I've tried the following :
#!/bin/bash -x
mytty=$(tty)
echo "mytty is ${mytty}"
myvar=$(scp test.js crashdog#webserver.com:/home/crashdog 2>&1 > $mytty)
echo "myvar is ${myvar}"
I can see the text on my tty like follows:
test.js 100% 1200 1.2KB/s 00:00
But myvar remains empty. So my two questions, where is above text coming from ? and how can I assign the text to a variable or array in Bash.
Thanks
Your problem is that scp doesn't output anything when its stdout isn't the terminal.
To trick it you can use script:
out=$(script -qefc "scp test.js crashdog#webserver.com:/home/crashdog" /dev/null)
echo "$out"
You can find more info about it in How to trick an application into thinking its stdout is a terminal, not a pipe
I'd like to know if the user can input data.
In a script, it is usually possible to call read -r VARIABLE to request input from the user. However, this doesn't work in all environments: for example, in CI scripts, it's not possible for the user to input anything, and I'd like to substitute a default value in that case.
So far, I'm handling this with a timeout, like this:
echo "If you are a human, type 'ENTER' now. Otherwise, automatic installation will start in 10 seconds..."
read -t 10 -r _user_choice || _user_choice="no-user-here"
But honestly, that just looks ugly.
The solution doesn't have to use read, however it needs to be portable to all major distros that have Bash, so it's not possible to use packages that are not installed by default.
$ cat stdin.bash
if [[ -t 0 ]]; then
echo "stdin is a terminal, so the user can input data"
else
echo "stdin is connected to some other redirect or pipeline"
fi
and, demonstrating
$ bash stdin.bash
stdin is a terminal, so the user can input data
$ echo foo | bash stdin.bash
stdin is connected to some other redirect or pipeline
From help test output:
-t FD True if FD is opened on a terminal.
I am writing shell script to install my application. I have more number of commands in my script such as copy, unzip, move, if and so on. I want to know the error if any of the commands fails. Also I don't want to send exit codes other than zero.
Order of script installation(root-file.sh):-
./script-to-install-mongodb
./script-to-install-jdk8
./script-to-install-myapplicaiton
Sample script file:-
cp sourceDir destinationDir
unzip filename
if [ true]
// success code
if
I want to know by using variable or any message if any of my scripts command failed in root-file.sh.
I don't want to write code to check every command status. Sometimes cp or mv command may fail due to invalid directory. At the end of script execution, I want to know all commands were executed successfully or error in it?
Is there a way to do it?
Note: I am using shell script not bash
/* the status of your last command stores in special variable $?, you can define variable for $? doing export var=$? */
unzip filename
export unzipStatus=$?
./script1.sh
export script1Status=$?
if [ !["$unzipStatus" || "$script1Status"]]
then
echo "Everything successful!"
else
echo "unsuccessful"
fi
Well as you are using shell script to achieve this there's not much external tooling. So the default $? should be of help. You may want to check for retrieval value in between the script. The code will look like this:
./script_1
retval=$?
if $retval==0; then
echo "script_1 successfully executed ..."
continue
else;
echo "script_1 failed with error exit code !"
break
fi
./script_2
Lemme know if this added any value to your scenario.
Exception handling in linux shell scripting can be done as follows
command || fallback_command
If you have multiple commands then you can do
(command_one && command_two) || fallback_command
Here fallback_command can be an echo or log details in a file etc.
I don't know if you have tried putting set -x on top of your script to see detailed execution.
Want to give my 2 cents here. Run your shell like this
sh root-file.sh 2> errors.txt
grep patterns from errors.txt
grep -e "root-file.sh: line" -e "script-to-install-mongodb.sh: line" -e "script-to-install-jdk8.sh: line" -e "script-to-install-myapplicaiton.sh: line" errors.txt
Output of above grep command will display commands which had errors in it along with line no. Let say output is:-
test.sh: line 8: file3: Permission denied
You can just go and check line no.(here it is 8) which had issue. refer this go to line no. in vi.
or this can also be automated: grep specific line from your shell script. grep line with had issue here it is 8.
head -8 test1.sh |tail -1
hope it helps.
I trying to create a .sh file that execute things like "pwd" or "ls" command.
My problem its when i execute the .sh file.
Its seems not recognize the tasks
I tried to use echo
Example : echo 'lsApps' or echo "lsApps"
but it prints the name of the task instead execute the comand
for example i want to execute a .ssh file that makes a pwd
VAR_1=pwd
echo $VAR_1
but it prints me pwd instead the current path ...
Any idea?
echo is used to print on the screen (man page reference). If you do echo 'IsApps' it will take it as a string and print it. If you want to execute a command you can just do it by doing IsApps (acutes not quotes, acute is usually below the escape key). This will execute the command and show the output on the screen. If you want to store the output of the command in a variable, you can do
<variable_name>=`IsApps`
This will store the output in the variable. Note that there is no space between variable name and the command. Also, those are not quotes but instead acutes. To print the variable on screen you can use echo by doing echo $<variable_name>
If you don't want to see the output at all. You can do
IsApps > /dev/null
this will execute the command but you will not see any stdout on your screen.
As far as ssh is concerned, do ssh-keygen and then ssh-copy-id user#remote_ip to set ssh keys so that you don't have to enter your password with ssh. Once you have done that, you can use ssh user#remote_ip in your shell script.
I have a shell script which writes all output to logfile
and terminal, this part works fine, but if I execute the script
a new shell prompt only appear if I press enter. Why is that and how do I fix it?
#!/bin/bash
exec > >(tee logfile)
echo "output"
First, when I'm testing this, there always is a new shell prompt, it's just that sometimes the string output comes after it, so the prompt isn't last. Did you happen to overlook it? If so, there seems to be a race where the shell prints the prompt before the tee in the background completes.
Unfortunately, that cannot fixed by waiting in the shell for tee, see this question on unix.stackexchange. Fragile workarounds aside, the easiest way to solve this that I see is to put your whole script inside a list:
{
your-code-here
} | tee logfile
If I run the following script (suppressing the newline from the echo), I see the prompt, but not "output". The string is still written to the file.
#!/bin/bash
exec > >(tee logfile)
echo -n "output"
What I suspect is this: you have three different file descriptors trying to write to the same file (that is, the terminal): standard output of the shell, standard error of the shell, and the standard output of tee. The shell writes synchronously: first the echo to standard output, then the prompt to standard error, so the terminal is able to sequence them correctly. However, the third file descriptor is written to asynchronously by tee, so there is a race condition. I don't quite understand how my modification affects the race, but it appears to upset some balance, allowing the prompt to be written at a different time and appear on the screen. (I expect output buffering to play a part in this).
You might also try running your script after running the script command, which will log everything written to the terminal; if you wade through all the control characters in the file, you may notice the prompt in the file just prior to the output written by tee. In support of my race condition theory, I'll note that after running the script a few times, it was no longer displaying "abnormal" behavior; my shell prompt was displayed as expected after the string "output", so there is definitely some non-deterministic element to this situation.
#chepner's answer provides great background information.
Here's a workaround - works on Ubuntu 12.04 (Linux 3.2.0) and on OS X 10.9.1:
#!/bin/bash
exec > >(tee logfile)
echo "output"
# WORKAROUND - place LAST in your script.
# Execute an executable (as opposed to a builtin) that outputs *something*
# to make the prompt reappear normally.
# In this case we use the printf *executable* to output an *empty string*.
# Use of `$ec` is to ensure that the script's actual exit code is passed through.
ec=$?; $(which printf) ''; exit $ec
Alternatives:
#user2719058's answer shows a simple alternative: wrapping the entire script body in a group command ({ ... }) and piping it to tee logfile.
An external solution, as #chepner has already hinted at, is to use the script utility to create a "transcript" of your script's output in addition to displaying it:
script -qc yourScript /dev/null > logfile # Linux syntax
This, however, will also capture stderr output; if you wanted to avoid that, use:
script -qc 'yourScript 2>/dev/null' /dev/null > logfile
Note, however, that this will suppress stderr output altogether.
As others have noted, it's not that there's no prompt printed -- it's that the last of the output written by tee can come after the prompt, making the prompt no longer visible.
If you have bash 4.4 or newer, you can wait for your tee process to exit, like so:
#!/usr/bin/env bash
case $BASH_VERSION in ''|[0-3].*|4.[0-3]) echo "ERROR: Bash 4.4+ needed" >&2; exit 1;; esac
exec {orig_stdout}>&1 {orig_stderr}>&2 # make a backup of original stdout
exec > >(tee -a "_install_log"); tee_pid=$! # track PID of tee after starting it
cleanup() { # define a function we'll call during shutdown
retval=$?
exec >&$orig_stdout # Copy your original stdout back to FD 1, overwriting the pipe to tee
exec 2>&$orig_stderr # If something overwrites stderr to also go through tee, fix that too
wait "$tee_pid" # Now, wait until tee exits
exit "$retval" # and complete exit with our original exit status
}
trap cleanup EXIT # configure the function above to be called during cleanup
echo "Writing something to stdout here"