I have a shell script written in bash and this script should take file as an argument,can any one tell me how to write script for this any ideas on this are apprecited
Thanks,
You can access the command line arguments passed to your script using positional parameters.
Also to check if the right number of arguments have been passed to the script, you can make use of the variable $# which holds the number of arguments passed.
if [ $# -eq 1 ]; then
# exactly 1 argument was passed..use it..its available in $1
echo "Argument $1"
else
# either 0 or >1 arguments were passed...error out.
echo "Incorrect number of arguments passed"
exit 1
fi
Sample run:
$ bash a.sh
Incorrect number of arguments passed
$ bash a.sh foo
Argument foo
$ bash a.sh foo bar
Incorrect number of arguments passed
$
If you need to operate on the file, you can take the name of the file as an argument and just use the file with the specified name.
If you just need to read the contents of the file, you can use redirection to have the script read the contents of the file on standard in. You can do this using ./script < inputfile
Related
Suppose I have a script named script1, and I want to pass to it one file named file in 1 (with 4 spaces between "file and "in")
So, I want to be able to pass this file name to the script without using "":
./script1 file in 1
instead of:
./script1 "file in 1"
I tried to use "$*" in the script to receive the file name correctly, but what the script receives is: file in 1 (the spaces are omitted)
Is there a way to do that in bash?
There is nothing you can or should do. Suppose your script is (correctly) written as
#!/bin/bash
cat "$1"
If your instructor expects your script to handle
./script file in 1
and
./script "file in 1"
identically, your instructor is profoundly mistaken about how the shell works.
The command:
./script file in 1
will execute ./scrip with three distinct input files: file, in, and 1. If you want ./scrip to read file in 1 as a single file but don't want to use quotes in the call, then you have some options:
#!/usr/bin/env bash
# Not ideal, as it also matches any number of characters
# between "file", "in" and "1": file--in--1
cat "$(ls $1*$2*$3)"
# Better but also matches single (or any number) of spaces: file in 1
cat "$(ls $1*$2*$3 | grep "$1 *$2 *$3")"
# Will only match: file in 1
cat "$(ls $1*$2*$3 | grep -E "$1 {4}$2 {1}$3")"
I need to read command line arguments. First arg is script name. second one is redirection operator i.e. "<" and third one is input filename. When I tried to use "$#", I got 0. When I used "$*", it gave me nothing. I have to use "<" this operator. My input file consists of all user input data. If I don't use the operator, It asks user for the input. Can someone please help me? Thank you !
Command Line :
./script_name < input_file
Script:
echo "$*" # gave nothing
echo "$#" # gave me 0
I need to read input filename and store it to some variable. Then I have to change the extension of it. Any help/suggestions should be appreciated.
When a user runs:
./script_name <input_file
...that's exactly equivalent to if they did the following:
(exec <input_file; exec ./script_name)
...first redirecting stdin from input_file, then invoking the script named ./script_name without any arguments.
There are operating-system-specific interfaces you can use to get the filename associated with a handle (when it has one), but to use one of these would make your script only able to run on an operating system providing that interface; it's not worth it.
# very, very linux-specific, won't work for "cat foo | ./yourscript", generally evil
if filename=$(readlink /proc/self/fd/0) && [[ -e $filename ]]; then
set -- "$#" "$filename" # append filename to the end of the argument list
fi
If you want to avoid prompting for input when an argument is given, and to have the filename of that argument, then don't take it on stdin but as an argument, and do the redirection yourself within the script:
#!/bin/bash
if [[ $1 ]]; then
exec <"$1" # this redirects your stdin to come from the file
fi
# ...put other logic here...
...and have users invoke your script as:
./script_name input_file
Just as ./yourscript <filename runs yourscript with the contents of filename on its standard input, a script invoked with ./yourscript filename which invokes exec <"$1" will have the contents of filename on its stdin after executing that command.
< is used for input redirection. And whatever is at the right side of < is NOT a command line argument.
So, when you do ./script_name < input_file , there will be zero (0) command line arguments passed to the script, hence $# will be zero.
For your puprpose you need to call your script as:
./script_name input_file
And in your script you can change the extension with something like:
mv -- "$1" "${1}_new_extension"
Edit: This was not what OP wanted to do.
Altough, there is already another spot on answer, I will write this for the sake of completeness. If you have to use the '<' redirection you can do something like this in your script.
while read filename; do
mv -- "$filename" "${filename}_bak"
done
And call the script as, ./script < input_file. However, note that you will not be able to take inputs from stdin in this case.
Unfortunately, if you're hoping to take redirection operators as arguments to your script, you're not going to be able to do that without surrounding your command line arguments in quotes:
./script_name "<input_file"
The reason for this is that the shell (at least bash or zsh) processes the command before ever invoking your script. When the shell interprets your command, it reads:
[shell command (./script_name)][shell input redirection (<input_file)]
invoking your script with quotes effectively results in:
[shell command (./script_name)][script argument ("<input_file")]
Sorry this is a few years late; hopefully someone will find this useful.
Say I let a user input as many arguments as he wants, how do read all the inputs?
So if a user typed in asdf asfd asdf, it should say 3 arguments
Right now I have
#!/bin/bash
read $#
echo "There are $# arguments"
but whenever I type in anything it always equals 0
If you want to read all the parameters from a variable using the same style as if you would have done it from the command line. You could do it in a function
#!/bin/bash
function param_count() {
for arg in $#
do
echo " arg: $arg"
done
echo "arg count= $#"
}
read foo
param_count $foo
If you really want to read a line as a sequence of words, your best bet in bash is to read it into an array:
$ read -a words
And now a few words from our sponsor
$ echo ${#words[#]}
8
$ echo "${words[3]}"
few
But that's not the way to pass arguments to a shell script. read just splits lines into words using whitespace (or whatever IFS is set to.) It does not:
Handle quotes
Allow the insertion of shell variables
Expand pathname patterns
Passing arguments on the command line lets you do all of the above, making the utility more convenient to use, and does not require any extra work to extract the arguments, making the utility easier to write.
This is the way to get it:
read ARGUMENTS
set -- $ARGUMENTS
echo "There are $# arguments"
Explanation:
read ARGUMENTS
Read the input and save it into ARGUMENTS
set -- $ARGUMENTS
Set the positional parameters to the given input
At this point, you can use that input as if it was given in the command-line.
e.g.:
echo "$1"
echo "$2"
...
Delete read $# line.
Then launch your script with arguments. Just like this:
/path/to/script arg1 arg2 arg3 4 5 6
The output should be:
There are 6 arguments
Here is an example script that shows you how to get the argument count, access the individual arguments and read in a new variable:
#!/usr/bin/env bash
echo "There are $# arguments, they are:"
for arg in $#
do
echo " - $arg"
done
# Access the aguments
echo "The first argument is $1"
echo "The second argument is $2"
echo "The third argument is $3"
echo "All arguments: $#"
# Read a variable
read var
echo "Some var: $var"
$ bash -c 'echo "0 is $0 1 is $1"' abc def
0 is abc 1 is def
$ echo 'echo "0 is $0 1 is $1"' > bashtest
$ bash bashtest abc def
0 is bashtest 1 is abc
The second run is equivalent to if I turned bash test into a shellscript with the shebang and then ran it directly...
Basically I'm wondering why abc isn't always $1. It becomes $0 when run with bash -c.
I also didn't know this. But the man page mentions it:
-c string: If the -c option is present, then commands are read from
string. If there are arguments after the string, they are
assigned to the positional parameters, starting with $0.
The ARGUMENTS section has an even more detailed explanation:
ARGUMENTS
If arguments remain after option processing, and neither the -c nor the
-s option has been supplied, the first argument is assumed to be the
name of a file containing shell commands. If bash is invoked in this
fashion, $0 is set to the name of the file, and the positional parame‐
ters are set to the remaining arguments. Bash reads and executes com‐
mands from this file, then exits. Bash's exit status is the exit sta‐
tus of the last command executed in the script. If no commands are
executed, the exit status is 0. An attempt is first made to open the
file in the current directory, and, if no file is found, then the shell
searches the directories in PATH for the script.
I know how to give default values to arguments in bash or exit with an error message if one is unset. (from here)
I want to make check if an argument was set, execute some code if it wasn't and the exit.
if [$1 is not set]; then
execute command
fi
I am writing a wrapper around another command. It has it's own usage message if no first argument is set (it is actually an input filename). It has other inputs that I am hardcoding for now. The actual command in the bash script is something like :
command $1 12 3124 534
I want to invoke it's own help message if no $1 was sent to the script.
Just check the length of arguments:
if (( $# < 1 )); then
echo 'There are no arguments, so you can bet that $1 was not set.'
fi
It's usually trivial to check if a variable has a non-empty value in shell:
[ -n "$val" ]
However, if you want to determine if a name has been declared I'll refer you to BashFAQ 83