I have done a simple shell script:
#!/bin/sh
file_name=$1
state=`cat "$file_name" | grep "port protocol" | awk '{print $4}'`
echo $state
reason=`cat "$file_name" | grep "port protocol"`
echo $reason
This outputs the values $state and $reason.
However, when I run..
echo $state
..in the console it does not output nothing. It seems like the variable loses its value. Is this the normal behaviour or should I had something to the script?
Thanks!
Assuming that you're running your script like ./script.sh or sh script.sh, then this is the expected behaviour. Child processes cannot change the environment of their parent. The fact that you're running a shell script from a shell doesn't change this rule.
What you can do is source the script instead of executing it, to set those variables in the local environment:
. script.sh
This effectively runs the lines of the script in your current shell, so the variables will be set there.
While I've got your attention, I'd recommend making the following changes to your script:
#!/bin/sh
file_name=$1
state=$(awk '/port protocol/ {print $4}' "$file_name")
echo "$state"
reason=$(grep "port protocol" "$file_name")
echo "$reason"
In short, quote your variables, avoid useless calls to cat and use pattern matching in awk rather than piping grep to it.
Related
Okay, I've written a shell script to read a file with the format:
shortcut1 /path/to/directory1
shortcut2 /path/to/directory2
and its supposed to read the file and build aliases such that typing shortcut1 cd's me into the mapped directory. The problem is, any of the aliases set in the loop don't persist outside of the script.
Firstly I tried just running the script.
. ./build_shortcuts.sh "~/.shortcuts"
where the file ~/.shortcuts contains
dl ~/Downloads
music /music
dtop ~/Desktop
This didn't work. Then I tried setting some aliases outside of the loop. Such as alias hello='world'; alias world='hellob'. I reran the script, typed alias to get a list of aliases and it did include hello and world as aliases, but not any of those set in the loop.
Then I thought maybe the loop isn't setting them at all, so I added alias as the final command in the script so it would print out the aliases at the end; in this case it did include the aliases but they still didn't persist in my session.
build_shortcuts.sh
script="$(cat $# | sed -r -e 's/#[A-Za-z0-9 ]*$//' -e '/^\s+/s/^\s+//' -e '/^\s*$/d' -)"
# strip comments, entry level indentation & empty lines (in that order) from filestream
echo "${script}" | while read shortcut; do
cut=$(echo "${shortcut}" | awk '{ print $1 }')
dest=$(echo "${shortcut}" | awk '{ $1=nil; print $0 }')
dest="${dest:1}" # trim leading whitespace character
alias "${cut}" &>/dev/null
if [ $? = 0 ]; then
echo "Warning: shortcut \"${cut}\" already exists" >&2
continue # by default, skip overwriting shortcuts
fi
echo alias ${cut}="'cd ${dest}'"
alias "${cut}"="'cd ${dest}'"
done
I want the aliases set in the loop within the script to exist outside of the script. Currently they don't.
I'm running on "GNU bash, version 5.0.7(1)-release (x86_64-pc-linux-gnu)" on arch linux.
From the Bash manual page (the section on Pipelines):
Each command in a pipeline is executed as a separate process (i.e., in a subshell)
Since the loop is done as part of a pipe, it will be a subshell, and the alias command you do in the subshell will only be set for that subshell.
A possible work-around would be to save the aliases to a list, and then perform the actual alias commands in a second loop, a loop which is not part of a pipe or a subshell.
Your script can be reduced a bit: it doesn't need to call out to so many external tools.
while read -r cut dest; do
if alias "${cut}" &>/dev/null; then
echo "Warning: shortcut \"${cut}\" already exists" >&2
else
echo alias ${cut}="'cd ${dest}'"
alias "${cut}"="'cd ${dest}'"
fi
done < <(
sed -E -e 's/#[A-Za-z0-9 ]*$//' -e '/^\s+/s/^\s+//' -e '/^\s*$/d' "$#"
)
after "done", I'm redirecting input from a process substitution: this avoids the "while read" loop from running in a subshell.
Given the variables listed below:
echo ${userupper}_PYTHON
# YTU_PYTHON
echo $YTU_PYTHON
# /home/ytu/anaconda3/bin/python
echo $path
# foo.py
Now I'd like to execute /home/ytu/anaconda3/bin/python foo.py with userupper and path. I tried $(${userupper}_PYTHON) $path but it ends up with error messages including:
YTU_PYTHON: not found
foo.py: not found
It seems like it takes $(${userupper}_PYTHON) as bare YTU_PYTHON rather than expected $YTU_PYTHON. How should I do to make it right?
Edits:
The suggested duplication should have solved my problem. However for some unknown reasons it's not working.
#!/usr/bin/env bash
for user in ytu
do
. /home/${user}/.profile
userupper=$(echo ${user} | awk '{print toupper($0)}')
userpython=${userupper}_PYTHON
cd /home/${user}/H2-ML/crons
for path in $(ls | grep ^${user}_.*_monthly_report.py$)
do
echo ${userpython}
echo $path
echo $YTU_PYTHON
echo ${!userpython}
done
done
The code chunk above returns:
YTU_PYTHON
ytu_clinic249_monthly_report.py
/home/ytu/anaconda3/bin/python
send_monthly_reports.sh: 14: send_monthly_reports.sh: Bad substitution
, which makes me so confused.
Try this:
command=${userupper}_PYTHON
echo ${!command} $path # ommit echo if you want to execute command
in your case it echos:
/home/ytu/anaconda3/bin/python foo.py
Here is link that might be useful if you want to write bash scripts. You should not parse ls output in the nutshell.
You also create unnecessary new process, both lines do the same:
$(echo ${user} | awk '{print toupper($0)}')
${user^^} #paramenter expansion
Doing a simple script in work and I can't figure out why it will not output the results in another file.
/tmp/system stores the node list
#!/bin/bash
$results=restest.txt
for i in $(cat /tmp/system); do
ping -c 1 $i
if [ $i = 0 ]; then
ssh $i ps -ef | grep ops | echo > $results
fi
done
echo is not printing from stdin but rather its arguments (or a newline if there no arguments). So
echo file.txt
only prints 'file.txt', not the content of the file. Therefore your code only writes a newline to the file. You can use cat for printing stdin to stdout, but here it is useless, since you can pipe the grep output directly to the file:
ssh $i ps -ef | grep ops > $results
first thank you for editing your code (it's more clear like this :)
I have 2 or 3 advices :
1- when you want to store a value in variable dont use "$" symbole, this symbole is used to get the variable's value
ex:
MYVAR=3
echo MYVAR "this will print MYVAR"
echo $MYVAR "this will print 3"
2- always quote your values, specially if the value is comming from another command
3- To fix your script you need to quote the command executed on the remote machine,
then redirect the output to your file
ex:
ssh user#MachineA "ls > file.txt" "file.txt is created on machineA"
ssh user#machineA "ls" > file.txt "file.txt is created on YOUR machine"
so simply you can replace your last line by
ssh $i "ps -ef | grep ops" > $results
try to use -ne in your test bash classictest
good luck
There are several errors. First, don't iterate over files like this. Second, i is the name of the host, not the exit status of ping. Third, the echo is unnecessary (and does not read from standard input). Lastly, $ is only used for expanding a parameter, not assigning to it.
#!/bin/bash
results=restest.txt
while read -r host; do
if ping -c 1 "$host"; then
ssh "$host" ps -ef | grep ops > "$results"
fi
done < /tmp/system
I have a shell script that I'm writing to search for a process by name and return output if that process is over a given value.
I'm working on finding the named process first. The script currently looks like this:
#!/bin/bash
findProcessName=$1
findCpuMax=$2
#echo "parameter 1: $findProcessName, parameter2: $findCpuMax"
tempFile=`mktemp /tmp/processsearch.XXXXXX`
#echo "tempDir: $tempFile"
processSnapshot=`ps aux > $tempFile`
findProcess=`awk -v pname="$findProcessName" '/pname/' $tempFile`
echo "process line: "$findProcess
`rm $tempFile`
The error is occuring when I try to pass the variable into the awk command. I checked my version of awk and it definitely does support the -v flag.
If I replace the '/pname/' portion of the findProcess variable assignment the script works.
I checked my syntax and it looks right. Could anyone point out where I'm going wrong?
The processSnapshot will always be empty: the ps output is going to the file
when you pass the pattern as a variable, use the pattern match operator:
findProcess=$( awk -v pname="$findProcessName" '$0 ~ pname' $tempFile )
only use backticks when you need the output of a command. This
`rm $tempFile`
executes the rm command, returns the output back to the shell and, it the output is non-empty, the shell attempts to execute that output as a command.
$ `echo foo`
bash: foo: command not found
$ `echo whoami`
jackman
Remove the backticks.
Of course, you don't need the temp file at all:
pgrep -fl $findProcessName
I am writing a simple bash script (I am fairly new to bash). I am trying to assign the output of a command to a variable and I can't seem to get it to work properly. See below:
#!/bin/bash
HOST=`hostname`
MEMORY=`prtconf -v |grep Memory |awk '{print $3}'`
echo "os_instance_name "$HOST
echo "physical_machine_ram "$MEMORY
I am able to get the result of hostname properly but my MEMORY variable is not being populated with the output from the command.
Some ideas spring to my mind:
execute the script in debug mode
$ bash -x myscript.sh
Put the variable expansions within the double quotes
echo "os_instance_name $HOST"
echo "physical_machine_ram $MEMORY"
Try this:
memory=`free -m | grep Mem | awk '{print $2}'`
echo $memory
should return the physical memory in megabytes
Also, $1 in the code that you have provided might be referring to a blank column. Try using $2.