I am trying to write a file that mimics the cat -n bash command. It is supposed to accept a filename as input and if no input is given print a usage statement.
This is what I have so far but I am not sure what I am doing wrong:
#!/bin/bash
echo "OK"
read filename
if [ $filename -eq 0 ]
then
echo "Usage: cat-n.sh file"
else
cat -n $filename
fi
I suggest to use -z to check for empty variable $filename:
if [ -z $filename ]
See: help test
Related
I have a placeholder file and would like to override it with the output of a command only if the output is not zero length. I guess I could do FOO="$(command)" then [-z $FOO]. Is there a better way?
There are different ways, but I don't know about "better". You could block on a read and only set up the redirection once some data come through:
cmd | { read j && { echo "$j"; cat; } > placeholder; }
(Note, if your command generates output but no newlines, this will ignore the data.)
If you don't need the output of the command, you can run: [ -z "$(command)" ] directly. For example, the following prints "empty":
#!/bin/sh
if [ -z "$(echo -n)" ]; then
echo "empty"
fi
Your example with -z will work, but you can also check if a variable is non-empty just with
[ "$var" ]
So, a simple solution could look like this:
#!/bin/sh
output="$( command )"
[ "$output" ] && echo "$output" > your_file.txt
If you are going to do this type of thing a lot, better make a function:
write_if_non_zero(){
local msg=$1
local file=$2
if [[ ! -z "$msg" ]]; then
echo "$msg" > "$file"
fi
}
Then
write_if_non_zero "$FOO" "$FILE"
Right. Basically what I want to achieve is i am trying to write a bash script which will take file as input argument and then load that file in the script and show all the lines in that file with the numbers bulletin. how can i do that.
i have tried nl -ba teat file but its not working
1 hii
2 whats up
3 how are you today
4 where have you been
5 whats going on
this is the sript i have written
#!/bin/bash
if [ $# == 0 ]; then
echo "ivalid argument"
elif [ $1 == $file ]; then
while read line
do
echo `nl $Line`
done
else
echo "wtf"
fi
See below modified script that does the job without using "nl":
#!/bin/bash
if [ -z "$1" ]; then
echo "ivalid argument"
exit 1;
fi;
cnt=1;
while read line; do
echo "$cnt $line";
((cnt++));
done<"$1";
I don't know, what
[ $1 == $file ]
is supposed to mean in your script, since you don't set a variable file anywhere, but here is one solution:
#!/bin/bash
nl -ba {$1?Tell me the name of the file or I will kick you}
Write a script that takes exactly one argument, a directory name. The script should print that argument back to standard output. Make sure the script generates a usage message if needed and that it handles errors with a message.
I write code, how i understand. Am i understand correctly this question? Maybe there are other versions how find directory.
#!/bin/bash
echo "Enter fail name:"
read str
find "$str" 2>/dev/null
sa=$?
if [ "$sa" = '0' ]
then
echo "$str"
else
echo "Error"
fi
Your script doesn't appear to be using an argument. In bash the first one ($0 is your script) would be $1 and something like,
#!/bin/bash
if [ "$1" == "" ]; then
echo "$0: Please provide a directory name"
exit 1
fi
if [ ! -d "$1" ]; then
echo "$0: $1 is not a directory name"
exit 1
fi
echo "$1"
Script needed was
#!/bin/bash
# Check if there are two arguments
if [ $# -eq 2 ]; then
# Check if the input file actually exists.
if ! [[ -f "$1" ]]; then
echo "The input file $1 does not exist."
exit 1
fi
else
echo "Usage: $0 [inputfile] [outputfile]"
exit 1
fi
# Run the command on the input file
grep -P "^[\s]*[0-9A-Za-z-]+.?[\s]*$" "$1" > "$2"
Edit, the script has changed to
grep -P "^[\s]*[0-9A-Za-z-]+.?[\s]*$" $*
if [ ! -f "$1" ]; then
echo 'Usage: '
echo
echo './Scriptname inputfile > outputfile'
exit 0
fi
invoking the script with no parameters gives no erros and sits blank
Usage:
./Scriptname inputfile > outputfile
I have bit of code
grep -P "^[\s]*[0-9A-Za-z-]+.?[\s]*$" $*
This code pulls lines that have a single word on them and pumps the output to a new file, so for example
This is a multi word line
this
the above line is not
now
once again wrong
The output would be
This
now
The code works, users invoke the code using ./scriptname file > newfile
However, I am trying to expand the code to give users an error message if they invoke the script incorrectly.
For the error messange, I'm thinking of echoing something back like scriptname file_to_process > output_file.
I did try
if [incorrectly invoted unsure what to type]
echo $usage
exit 1
Usage="usage [inputfile] [>] [outputfile]
However I have had little luck. The code runs but does nothing if I invoke with just the script name. Also, if I invoke the script with just the scriptname and the input file, it will output the results instead of exiting with the error message.
Other ones I have tried are
if [ ! -n $1 ]; then
echo 'Usage: '
echo
echo './Scriptname inputfile > outputfile'
exit 0
fi
Given replies I have received so far, my code now is
#!/bin/bash
grep -P "^[\s]*[0-9A-Za-z-]+.?[\s]*$" $*
if [ ! -f "$1" ]; then
echo 'Usage: '
echo
echo './Scriptname inputfile > outputfile'
exit 0
fi
When invoking the script without an input file the script does nothing and has to be aborted with ctrl+c, still trying to get the echo of the invoke message.
When you are invoking the script like ./scriptname file > newfile, the shell interprets file as the only argument to ./scriptname. This is because > is the standard output redirection operator.
I would like to propose 2 possible alternatives:
Alternative 1:
Maybe you're can try passing it as 1 argument like this?
./scriptname 'file > newfile'
In that case one way to check the format would be
#!/bin/bash
# Check if the format is correct
if [[ $1 =~ (.+)' > '(.+) ]]; then
# Check if the input file actually exists.
if ! [[ -f "${BASH_REMATCH[1]}" ]]; then
echo "The input file ${BASH_REMATCH[1]} does not exist!"
exit 1
fi
else
echo "Usage: $0 \"[inputfile] [>] [outputfile]\""
exit 1
fi
# Redirect standard output to the output file
exec > "${BASH_REMATCH[2]}"
# Run the command on the input file
grep -P "^[\s]*[0-9A-Za-z-]+.?[\s]*$" "${BASH_REMATCH[1]}"
Note: If you are checking whether the arguments are valid or not, it's generally better to run commands only after the checking is done.
Alternative 2:
Passing 2 arguments like
./scriptname file newfile
The script looks like this
#!/bin/bash
# Check if there are two arguments
if [ $# -eq 2 ]; then
# Check if the input file actually exists.
if ! [[ -f "$1" ]]; then
echo "The input file $1 does not exist."
exit 1
fi
else
echo "Usage: $0 [inputfile] [outputfile]"
exit 1
fi
# Run the command on the input file
grep -P "^[\s]*[0-9A-Za-z-]+.?[\s]*$" "$1" > "$2"
I'd use parameter expansion for this:
inputfile=${1:?Usage: $(basename $0) inputfile > outputfile}
If the script is called without arguments (i.e. $1 is unset) the ${var:?error message} expansion causes the shell to display an error with the given message and exit. Otherwise the first argument is assigned to $inputfile.
Try to add double quotes around $1 and use -f to check for exists and is normal file:
if [ ! -f "$1" ]; then
echo 'Usage: '
echo
echo './Scriptname inputfile > outputfile'
exit 0
fi
Also you can check for the param count with $# and cat an usage message:
if [ ! $# -eq 1 ]; then
cat << EOF
Usage:
$0 'input_file' > output_file
EOF
exit 1
fi
I need some help getting this running. just two sections.
I'm a novice at bash scripting. how do I repeat this until a single filename is entered? PS it doens't seem to work with a while loop so far that I've tried.
ifSpaces(){
#if more than one file name if statement(checks for spaces)
if [ "$#" -eq "$(echo "$#" | wc -w)" ]
then
echo -n "Please enter a filename: ";
read filename
else
echo -n "Please enter a single filename! ";
fi
}
this function is supposed to test the file if it can be written. It seems to pass through it and not test it right. but I'm not really sure. Basically, what's wrong with it, please make corrections because I don't get it when people tell me and not show me how.
#how do I get this to work?
testFiles(){
#loop through files and test each one
for filename in "$#"
do
filename="$#"
# put this in a loop that grabs all the values.
# test all the file names
while [ -f "$filename" ]
do
if [ -w $filename ]
then
echo "The file exists and is writable";
overWriteFile
saveResults
elif [ -d $filename ]
then
read filename
echo "$filename";
echo "The file you specified exists and is a directory".
saveResults
else
>$directory$filename;
fi
done
echo "$filename";
echo "The file you specified exists and is neither a regular file nor a directory.";
done
saveResults
}
The ifSpaces() function needs better definition - what is it to do?
Takes one argument, a possible file name.
If that argument contains no spaces, return it.
If that argument contains spaces, prompt for a new file name until a name without spaces is given.
How to test for spaces in a string (aka filename)?
spacesInName()
{
case "$1" in
(* *) return 0;;
(*) return 1;;
esac
}
Hence:
ifSpaces()
{
filename=$1
while spacesInName "$filename"
do
echo -n "Enter a filename without spaces: "
read filename
done
return $filename
}
OK - that done, now we ask "why?". Wouldn't it be better just to make sure your code works with any valid file name, because all the main operating systems (file systems) recognize file names with spaces in them as valid?
Additionally, shell scripts that go prompting are often rather uncivilized; they certainly cannot be used reliably when there isn't a user to provide the input, and they cannot be used reliably in pipelines of commands. That severely limits their usefulness.
So, general purpose scripts don't ask questions. Special purpose scripts can ask questions. And it does depend on what the script is designed to do and who is going to use it. But avoid chattiness whenever possible.
You second function is similarly very confused. It isn't very clear what it is supposed to do, but this looks more plausible than the original:
testFiles()
{
for filename in "$#"
do
if [ -w "$filename" ]
then
echo "The file $filename exists and is writable";
saveResults "$filename"
elif [ -d "$filename" ]
then
echo "The file $filename exists and is a directory".
elif [ -f "$filename" ]
then
echo "The file $filename exists but is not writable"
else
echo "Either $filename does not exist or it is neither"
echo "a file nor a directory"
fi
done
}
Revised to prompt once per missing file for an answer - create the file...
testFiles()
{
for filename in "$#"
do
if [ -w "$filename" ]
then
echo "The file $filename exists and is writable";
saveResults "$filename"
elif [ -d "$filename" ]
then
echo "The file $filename exists and is a directory".
elif [ -f "$filename" ]
then
echo "The file $filename exists but is not writable"
elif [ ! -e "$filename ]
then
echo "$filename does not exist - create it? "
read yesno
case "$yesno" in
([Yy]*) cp /dev/null "$filename"
saveResults "$filename"
;;
(*) echo "OK - ignoring $filename"
;;
esac
fi
done
}