This simple program supposed to read the file lines, but instead it outputs "cat" every time. What is the problem?
#!/bin/sh
while read line
do
echo $line
done <file
Edit:
file is supposed to be the users input file when calling the program from the terminal. Like:
./programname file
this is suppose to be the users input file when calling the program
from the terminal. Like: ./programname file
In this case you should be doing
#!/bin/sh
if [ -f "$1" ] # checking if file exist
then
while read line
do
echo "$line"
done <"$1" # double quotes important to prevent word splitting
else
echo "Sorry file $1 doesn't exist"
fi
Here $1 represents the first parameter that you pass to the script.
Interesting reads:
What is [ word splitting ] ?
Shell script [ parameters ]
Related
I'm reading some file paths and names from a text file and trying to test if file exists. I'm not sure what I'm doing wrong but first echo returns filepath and file name whilst the echo inside the if statement doesn't. Any ideas?
#!/bin/bash
while read line; do
echo $line
if [ -f "$line" ]; then
echo "found: $line"
fi
done < /mbackup/temp/images.txt
The only change is adding the -r option to read. That option is documented as:
Backslash does not act as an escape character. The backslash is considered to be part of the line. In particular, a backslash-newline pair may not then be used as a line continuation.
This helps prevent special characters in file names from interfering with your script.
I test this with files containing special characters and it works as you expected.
#!/bin/bash
while read -r line; do
echo $line
if [ -f "$line" ]; then
echo "found: $line"
fi
done < /mbackup/temp/images.txt
How come this while loop only executes once even though there are 2 entries (lines) in the .csv file?
And how come when no files/directories match the expression, the string 'ls: /root/heapdump: No such file or directory' is output to console also?
#!/bin/bash
INPUT=targets.csv
[ ! -f $INPUT ] && { echo "$INPUT file not found"; exit 99; }
while IFS=, read target user password path
do
result=$(sshpass -p "$password" ssh "$user"#"$target" ls "$path"*heapdump*)
if [ $? -ne 0 ]
then
echo "No Heap dumps detected."
else
echo "Found a Heap dump! Possible OOM issue detected"
fi
done < $INPUT
.csv contents ..
rob#laptop:~/scripts$ cat targets.csv
server.com,root,passw0rd,/root/
server.com,root,passw0rd,/root/
Output ..
rob#laptop:~/scripts$ ./checkForHeapdump.sh
ls: /root/*heapdump*: No such file or directory
No Heap dumps detected.
ssh is reading from stdin, so it's slurping up the rest of the CSV file during the first iteration. Change to:
result=$(sshpass -p "$password" ssh -n "$user"#"$target" ls "$path"*heapdump*)
The -n option redirects ssh stdin to /dev/null.
Regarding the error message, that's the normal error output when you do:
ls filename-or-wildcard
and there are no matches. If you don't want to see that, redirect stderr:
result=$(sshpass -p "$password" ssh -n "$user"#"$target" ls "$path"*heapdump* 2>/dev/null)
I am guessing the final line of your CSV file is not terminated with a carriage return.
When this happens read will populate the variables but without the carriage return to terminate the line it will instead reach the end of file and so the loop will not execute.
If you cannot confirm the final line will be terminated with a carriage return, and are unable to edit the file by putting echo >> targets.csv at the start of the script to add one, then you can check the variables after the loop to act on them. To do this I would clear one of the variables at the end of the loop so then you can simply test whether it is non-zero length to know there is one final unprocessed line.
I have a number of bash scripts, each doing its own thing merrily. Do note that while I program in other languages, I only use Bash to automate things, and am not very good at it.
I'm now trying to combine a number of them to create "meta" scripts, if you will, which use other scripts as steps. The problem is that I need to parse the output of each step to be able to pass a part of it as params to the next step.
An example:
stepA.sh
[...does stuff here...]
echo "Task complete successfuly"
echo "Files available at: $d1/$1"
echo "Logs available at: $d2/$1"
both the above are paths, such as /var/www/thisisatest and /var/log/thisisatest (note that files always start with /var/www and logs always start with /var/log ). I'm only interested in the files path.
steB.sh
[...does stuff here...]
echo "Creation of $d1 complete."
echo "Access with username $usr and password $pass"
all variables here are simple strings, that may contain special characters (no spaces)
What I'm trying to build is a script that runs stepA.sh, then stepB.sh and uses the output of each to do its own stuff. What I'm currently doing (both above scripts are symlinked to /usr/local/bin without the .sh part and made executable):
#!/bin/bash
stepA $1 | while read -r line; do
# Create the container, and grab the file location
# then pass it to then next pipe
if [[ "$line" == *:* ]]
then
POS=`expr index "$line" "/"`
PTH="/${line:$POS}"
if [[ "$PTH" == *www* ]]
then
#OK, have what I need here, now what?
echo $PTH;
fi
fi
done
# Somehow get $PTH here
stepB $1 | while read -r line; do
...
done
#somehow have the required strings here
I'm stuck in passing the PTH to the next step. I understand this is because piping runs it in a subshell, however all examples I've seen refer to to files and not commands, and I could not make this to work. I tried piping the echo to a "next step" such as
stepA | while ...
echo $PTH
done | while ...
#Got my var here, but cannot run stuff
done
How can I run stepA and have the PTH variable available for later?
Is there a "better way" to extract the path I need from the output than nested ifs ?
Thanks in advance!
Since you're using bash explicitly (in the shebang line), you can use its process substitution feature instead of a pipe:
while read -r line; do
if [[ "$line" == *:* ]]
.....
fi
done < <(stepA $1)
Alternately, you could capture the command's output to a string variable, and then parse that:
output="$(stepA $1)"
tmp="${output#*$'\nFiles available at: '}" # output with everything before the filepath trimmed
filepath="${tmp%%$'\n'*}" # trim the first newline and everything after it from $tmp
tmp="${output#*$'\nLogs available at: '}"
logpath="${tmp%%$'\n'*}"
the following script is working fine on one server but on the other it gives an error
#!/bin/bash
processLine(){
line="$#" # get the complete first line which is the complete script path
name_of_file=$(basename "$line" ".php") # seperate from the path the name of file excluding extension
ps aux | grep -v grep | grep -q "$line" || ( nohup php -f "$line" > /var/log/iphorex/$name_of_file.log & )
}
FILE=""
if [ "$1" == "" ]; then
FILE="/var/www/iphorex/live/infi_script.txt"
else
FILE="$1"
# make sure file exist and readable
if [ ! -f $FILE ]; then
echo "$FILE : does not exists. Script will terminate now."
exit 1
elif [ ! -r $FILE ]; then
echo "$FILE: can not be read. Script will terminate now."
exit 2
fi
fi
# read $FILE using the file descriptors
# $ifs is a shell variable. Varies from version to version. known as internal file seperator.
# Set loop separator to end of line
BACKUPIFS=$IFS
#use a temp. variable such that $ifs can be restored later.
IFS=$(echo -en "\n")
exec 3<&0
exec 0<"$FILE"
while read -r line
do
# use $line variable to process line in processLine() function
processLine $line
done
exec 0<&3
# restore $IFS which was used to determine what the field separators are
IFS=$BAKCUPIFS
exit 0
i am just trying to read a file containing path of various scripts and then checking whether those scripts are already running and if not running them. The file /var/www/iphorex/live/infi_script.txt is definitely present. I get the following error on my amazon server-
[: 24: unexpected operator
infinity.sh: 32: cannot open : No such file
Thanks for your helps in advance.
You should just initialize file with
FILE=${1:-/var/www/iphorex/live/infi_script.txt}
and then skip the existence check. If the file
does not exist or is not readable, the exec 0< will
fail with a reasonable error message (there's no point
in you trying to guess what the error message will be,
just let the shell report the error.)
I think the problem is that the shell on the failing server
does not like "==" in the equality test. (Many implementations
of test only accept one '=', but I thought even older bash
had a builtin that accepted two '==' so I might be way off base.)
I would simply eliminate your lines from FILE="" down to
the end of the existence check and replace them with the
assignment above, letting the shell's standard default
mechanism work for you.
Note that if you do eliminate the existence check, you'll want
to either add
set -e
near the top of the script, or add a check on the exec:
exec 0<"$FILE" || exit 1
so that the script does not continue if the file is not usable.
For bash (and ksh and others), you want [[ "$x" == "$y" ]] with double brackets. That uses the built-in expression handling. A single bracket calls out to the test executable which is probably barfing on the ==.
Also, you can use [[ -z "$x" ]] to test for zero-length strings, instead of comparing to the empty string. See "CONDITIONAL EXPRESSIONS" in your bash manual.
I am writing a bash script to search for a pattern in a file using GREP. I am clueless for why it isnt working. This is the program
echo "Enter file name...";
read fname;
echo "Enter the search pattern";
read pattern
if [ -f $fname ]; then
result=`grep -i '$pattern' $fname`
echo $result;
fi
Or is there different approach to do this ?
Thanks
(contents of file)
Welcome to UNIX
The shell is a command programming language that provides an interface to the UNIX operating system.
The shell can modify the environment in which commands run.
Simple UNIX commands consist of one or more words separated by blanks.
Most commands produce output on the standard output that is initially connected to the terminal. This output may be sent to a file by writing.
The standard output of one UNIX command may be connected to the standard input of another UNIX Command by writing the `pipe' operator, indicated by |
(pattern)
`UNIX` or `unix`
The single quotes around $pattern in the grep statement make the shell not resolve the shell variable so you should use double quotes.
Only one of those semicolons is necessary (the one before then), but I usually omit it and put then on a line by itself. You should put double quotes around the variable that you're echoing and around the variable holding your grep pattern. Variables that hold filenames should be quoted, also. You can have read display your prompt. You should use $() instead of backticks.
read -p "Enter file name..." fname
read -p "Enter the search pattern" pattern
if [ -f "$fname" ]
then
result=$(grep -i "$pattern" "$fname")
echo "$result"
fi
read -p "Enter file name..." fname
read -p "Enter the search pattern" pattern
if [ -f "$fname" ]
then
result=$(grep -i -v -e $pattern -e "$fname")
echo "$result"
fi