Reverse Command Line Parameters in a bash script - bash

I have to write a simple bash script for my programming class. The idea is to use a for loop with $* (names of Files as Command Line Parameters). The task is to reverse and print out the Command Line parameters while still using the for inFile in $*; do loop.
I have no idea how to do this.
#!/bin/bash
for inFile in $*;do
echo $inFile
done
I know this doesn't work it just prints out the command line parameters in order.

The idea to loop over $* to reverse command line arguments is broken,
when any command line argument contains a white space. For example when the command line arguments are foo and "bar baz", the output of the script in the question will be:
foo
bar
baz
When the correct output should be:
foo
bar baz
The exact wording of the task is important.
For example, if the task is to print the arguments in reverse, and it doesn't mention $*, then you can use a counting loop in reverse, and ${!i} to expand to the value of the numbered positional parameters:
# nice clean solution
for ((i = $#; i > 0; i--)); do
echo "${!i}"
done
Another example, if the task insists that you must use $* and accepts that the command line arguments will only have supported characters, then you could collect the parameters into an array, and then print the content of the array in reverse, again with a counting loop:
args=()
# not recommended, unsafe due to shell expansion of $*
for arg in $*; do
args+=("$arg")
done
for ((i = ${#args[#]} - 1; i >= 0; i--)); do
echo "${args[i]}"
done
If you are not allowed to use arrays, then you can prepend values to a string, and then iterate over that string:
# dirtiest solution, with unsafe expansions and unquoted variables, not recommended
args=
for arg in $*; do
args="$arg $args"
done
for arg in $args; do
echo "$arg"
done

Related

Parsing command line arguments in a function [duplicate]

This question already has answers here:
How to access command line arguments of the caller inside a function?
(10 answers)
Closed 5 years ago.
Inside a function, $1 ... $n are the parameters passed to that function.
Outside a function $1 ... $n are the parameters passed to the script.
Can I somehow access the parameters passed to the script inside a function?
Usually you just pass them as parameters to the function at call time.
The (uglier) alternative is to put them in global variables.
(I know this is an old post, but none of the answers actually answered the question.)
Use the BASH_ARGV array. It contains the arguments passed to the invoking script in reverse order (i.e., it's a stack with the top at index 0). You may have to turn on extended debugging in the shebang (e.g., #!/bin/bash -O extdebug) or with shopt (e.g., shopt -s extdebug), but it works for me in bash 4.2_p37 without it turned on.
From man bash:
An array variable containing all of the parameters in the current bash execution call stack. The final parameter of the last subroutine call is at the top of the stack; the first parameter of the initial call is at the bottom. When a subroutine is executed, the parameters supplied are pushed onto BASH_ARGV. The shell sets BASH_ARGV only when in extended debugging mode….
Here's a function I use to print all arguments in order on a single line:
# Print the arguments of the calling script, in order.
function get_script_args
{
# Get the number of arguments passed to this script.
# (The BASH_ARGV array does not include $0.)
local n=${#BASH_ARGV[#]}
if (( $n > 0 ))
then
# Get the last index of the args in BASH_ARGV.
local n_index=$(( $n - 1 ))
# Loop through the indexes from largest to smallest.
for i in $(seq ${n_index} -1 0)
do
# Print a space if necessary.
if (( $i < $n_index ))
then
echo -n ' '
fi
# Print the actual argument.
echo -n "${BASH_ARGV[$i]}"
done
# Print a newline.
echo
fi
}
As Benoit stated, the simplest solution is to pass the commandline arguments to the function as function arguments with $#, then you can reference them in exactly the same way as outside the function. You'll actually be referencing the values passed to the function that just happen to have the same value as the commandline arguments, keep that in mind.
Note that this pretty much precludes you from passing any other arguments to the function, unless you know exactly how many arguments will be passed at the command line (unlikely as that is up to the user and isn't bound by your constraints)
i.e.
function fname {
# do something with $1 $2 $3...$n #
}
# $# represents all the arguments passed at the command line #
fname $#
A better way is to pass only the arguments you know you will be using, that way you can use them in the function AND also pass other parameters from within your code if you wish
i.e.
function fname {
# do something with $1 $count $2 and $3 #
}
count=1
fname $1 $count $2 $3
You can store all of your script arguments in a global array:
args=("$#")
and then access them in a function:
f(){
echo ${args[0]} ${args[1]}
}
You should probably use "$#" and pass that at the end of your function's argument list. Inside the function, shift after parsing your arguments and use $1 to $n as normally.
Thanks for the tips - they inspired me to write a callstack function. I used the 'column' command for esthetics.
callstack() {
local j=0 k prog=$(basename $0)
for ((i=1; ((i<${#BASH_ARGC[*]})); i++))
do
echo -n "${FUNCNAME[$i]/main/$prog} " # function name
args=""
for ((k=0; ((k<${BASH_ARGC[$i]})); k++))
do
args="${BASH_ARGV[$j]} $args" # arguments
let j++
done
echo -e "$args\t|${BASH_LINENO[$i]}" $(sed -n ${BASH_LINENO[$i]}p "$0" 2>/dev/null) # line calling the function
done | column -t -s $'\t' -o ' ' | sed 1d # delete callstack entry
}
compareTemplates brother_001270_1.jpg |163 compareTemplates "$f" # process the rest
processPdf brother_001270.pdf |233 filetype "${f%[*}" pdf && processPdf "$f"
process brother_001270.pdf |371 --process) shift; process "$#"; exit ;; # process jpg or pdf
sm --quiet --process brother_001270.pdf |0

How to use bash substitution to append a newline at the end of each element of a list

I am looking for a bash one liner that appends a newline after each element of a list. If I call the script as:
./script arg1 arg2 arg3
I want the output to be
arg1
arg2
arg3
I tried different variations of the following. The newline does not get added. Any ordinary char gets added.
# pretty much works except for an extra space
list=${#/%/x}
echo "$list"
# appends 'n'
list=${#/%/\n}
echo "$list"
# appends nothing
list=${#/%/$'\n'}
echo "$list"
# appends nothing, \x078 would append 'x'
list=${#/%/$'\x0D'}
echo "$list"
# appends nothing
CR=$'\n'
list=${#/%/$CR}
echo "$list"
# same issues with arrays
tmp=($#)
list=${tmp/%/\n}
echo "$list"
What fix or alternative do you suggest? I obviously could write a loop or call tr but that's precisely what I thought I could avoid with a bash substitution.
You can use this function with "$#":
f() { printf "%s\n" "$#"; }
f arg1 arg2 arg3
arg1
arg2
arg3
As per man bash
# Expands to the positional parameters, starting from one. When the expansion occurs
within double quotes, each parameter expands to a separate word. That is, "$#" is
equivalent to "$1" "$2" ...
printf would have been my answer as well. Another technique is to use IFS:
$ IFS=$'\n'
$ list="$*"
$ echo "$list"
arg1
arg2
arg3
Notes:
uses ANSI-C quoting for the newline sequence
"$*" (with the quotes, crucial) joins the positional params using the first char of $IFS
quote the shell variable for the echo command to preserve the inner newlines.
That redefines the IFS value for the current shell. You can save the old value first and restore it after:
oldIFS=$IFS; IFS=$'\n'; list="$*"; IFS=$oldIFS
or you can use a subshell so the modification is discarded for you:
$ list=$( IFS=$'\n'; echo "$*" )

Unix Shell equivalency to Java .hasNext()?

Or anything in shell script to implement the same thing?
I was doing an assignment that requires us to write a Bourne shell script that shows the last argument of a bunch, e.g.:
lastarg arg1 arg2 arg3 ..... argN
which would show:
argN
I was not sure if there's any equivalencies to hasNext in Java as it's easy to implement.
Sorry if I was rude and unclear.
#!/bin/bash
all=($#)
# to make things short:
# you can use what's in a variable as a variable name
last=$(( $# )) # get number of arguments
echo ${!last} # use that to get the last argument. notice the !
# while the number of arguments is not 0
# put what is in argument $1 into next
# move all arguments to the left
# $1=foo $2=bar $4=moo
# shift
# $1=bar $2=moo
while [ $# -ne 0 ]; do
next=$1
shift
echo $next
done
# but the problem was the last argument...
# all=($#): put all arguments into an array
# ${all[n]}: get argument number n
# $(( 1+2 )): do math
# ${#all[#]}: get the count of element in an array
echo -e "all:\t ${all[#]}"
echo -e "second:\t ${all[1]}"
echo -e "fifth:\t ${all[4]}"
echo -e "# of elements:\t ${#all[#]}"
echo -e "last element:\t ${all[ (( ${#all[#]} -1 )) ]}"
ok, last edit (omg :p)
$ sh unix-java-hasnext.sh one two three seventyfour sixtyeight
sixtyeight
one
two
three
seventyfour
sixtyeight
all: one two three seventyfour sixtyeight
second: two
fifth: sixtyeight
# of elements: 5
last element: sixtyeight
POSIX-based shell languages don't implement iterators.
The only things you have are for V in words ; do ... ; done or implementing the loop with while and manual stuff to update and test the loop variable.
This is not place to make wild guesses, still: Bash provide shift operator, for loop and more.
(If this is for argument processing you have getopt library. More info in Using getopts in bash shell script to get long and short command line options )

Access arguments to Bash script inside a function [duplicate]

This question already has answers here:
How to access command line arguments of the caller inside a function?
(10 answers)
Closed 5 years ago.
Inside a function, $1 ... $n are the parameters passed to that function.
Outside a function $1 ... $n are the parameters passed to the script.
Can I somehow access the parameters passed to the script inside a function?
Usually you just pass them as parameters to the function at call time.
The (uglier) alternative is to put them in global variables.
(I know this is an old post, but none of the answers actually answered the question.)
Use the BASH_ARGV array. It contains the arguments passed to the invoking script in reverse order (i.e., it's a stack with the top at index 0). You may have to turn on extended debugging in the shebang (e.g., #!/bin/bash -O extdebug) or with shopt (e.g., shopt -s extdebug), but it works for me in bash 4.2_p37 without it turned on.
From man bash:
An array variable containing all of the parameters in the current bash execution call stack. The final parameter of the last subroutine call is at the top of the stack; the first parameter of the initial call is at the bottom. When a subroutine is executed, the parameters supplied are pushed onto BASH_ARGV. The shell sets BASH_ARGV only when in extended debugging mode….
Here's a function I use to print all arguments in order on a single line:
# Print the arguments of the calling script, in order.
function get_script_args
{
# Get the number of arguments passed to this script.
# (The BASH_ARGV array does not include $0.)
local n=${#BASH_ARGV[#]}
if (( $n > 0 ))
then
# Get the last index of the args in BASH_ARGV.
local n_index=$(( $n - 1 ))
# Loop through the indexes from largest to smallest.
for i in $(seq ${n_index} -1 0)
do
# Print a space if necessary.
if (( $i < $n_index ))
then
echo -n ' '
fi
# Print the actual argument.
echo -n "${BASH_ARGV[$i]}"
done
# Print a newline.
echo
fi
}
As Benoit stated, the simplest solution is to pass the commandline arguments to the function as function arguments with $#, then you can reference them in exactly the same way as outside the function. You'll actually be referencing the values passed to the function that just happen to have the same value as the commandline arguments, keep that in mind.
Note that this pretty much precludes you from passing any other arguments to the function, unless you know exactly how many arguments will be passed at the command line (unlikely as that is up to the user and isn't bound by your constraints)
i.e.
function fname {
# do something with $1 $2 $3...$n #
}
# $# represents all the arguments passed at the command line #
fname $#
A better way is to pass only the arguments you know you will be using, that way you can use them in the function AND also pass other parameters from within your code if you wish
i.e.
function fname {
# do something with $1 $count $2 and $3 #
}
count=1
fname $1 $count $2 $3
You can store all of your script arguments in a global array:
args=("$#")
and then access them in a function:
f(){
echo ${args[0]} ${args[1]}
}
You should probably use "$#" and pass that at the end of your function's argument list. Inside the function, shift after parsing your arguments and use $1 to $n as normally.
Thanks for the tips - they inspired me to write a callstack function. I used the 'column' command for esthetics.
callstack() {
local j=0 k prog=$(basename $0)
for ((i=1; ((i<${#BASH_ARGC[*]})); i++))
do
echo -n "${FUNCNAME[$i]/main/$prog} " # function name
args=""
for ((k=0; ((k<${BASH_ARGC[$i]})); k++))
do
args="${BASH_ARGV[$j]} $args" # arguments
let j++
done
echo -e "$args\t|${BASH_LINENO[$i]}" $(sed -n ${BASH_LINENO[$i]}p "$0" 2>/dev/null) # line calling the function
done | column -t -s $'\t' -o ' ' | sed 1d # delete callstack entry
}
compareTemplates brother_001270_1.jpg |163 compareTemplates "$f" # process the rest
processPdf brother_001270.pdf |233 filetype "${f%[*}" pdf && processPdf "$f"
process brother_001270.pdf |371 --process) shift; process "$#"; exit ;; # process jpg or pdf
sm --quiet --process brother_001270.pdf |0

How to access command line arguments of the caller inside a function?

I'm attempting to write a function in bash that will access the scripts command line arguments, but they are replaced with the positional arguments to the function. Is there any way for the function to access the command line arguments if they aren't passed in explicitly?
# Demo function
function stuff {
echo $0 $*
}
# Echo's the name of the script, but no command line arguments
stuff
# Echo's everything I want, but trying to avoid
stuff $*
If you want to have your arguments C style (array of arguments + number of arguments) you can use $# and $#.
$# gives you the number of arguments.
$# gives you all arguments. You can turn this into an array by args=("$#").
So for example:
args=("$#")
echo $# arguments passed
echo ${args[0]} ${args[1]} ${args[2]}
Note that here ${args[0]} actually is the 1st argument and not the name of your script.
My reading of the Bash Reference Manual says this stuff is captured in BASH_ARGV,
although it talks about "the stack" a lot.
#!/bin/bash
shopt -s extdebug
function argv {
for a in ${BASH_ARGV[*]} ; do
echo -n "$a "
done
echo
}
function f {
echo f $1 $2 $3
echo -n f ; argv
}
function g {
echo g $1 $2 $3
echo -n g; argv
f
}
f boo bar baz
g goo gar gaz
Save in f.sh
$ ./f.sh arg0 arg1 arg2
f boo bar baz
fbaz bar boo arg2 arg1 arg0
g goo gar gaz
ggaz gar goo arg2 arg1 arg0
f
fgaz gar goo arg2 arg1 arg0
#!/usr/bin/env bash
echo name of script is $0
echo first argument is $1
echo second argument is $2
echo seventeenth argument is $17
echo number of arguments is $#
Edit: please see my comment on question
Ravi's comment is essentially the answer. Functions take their own arguments. If you want them to be the same as the command-line arguments, you must pass them in. Otherwise, you're clearly calling a function without arguments.
That said, you could if you like store the command-line arguments in a global array to use within other functions:
my_function() {
echo "stored arguments:"
for arg in "${commandline_args[#]}"; do
echo " $arg"
done
}
commandline_args=("$#")
my_function
You have to access the command-line arguments through the commandline_args variable, not $#, $1, $2, etc., but they're available. I'm unaware of any way to assign directly to the argument array, but if someone knows one, please enlighten me!
Also, note the way I've used and quoted $# - this is how you ensure special characters (whitespace) don't get mucked up.
# Save the script arguments
SCRIPT_NAME=$0
ARG_1=$1
ARGS_ALL=$*
function stuff {
# use script args via the variables you saved
# or the function args via $
echo $0 $*
}
# Call the function with arguments
stuff 1 2 3 4
One can do it like this as well
#!/bin/bash
# script_name function_test.sh
function argument(){
for i in $#;do
echo $i
done;
}
argument $#
Now call your script like
./function_test.sh argument1 argument2
This is #mcarifio response with several comments incorporated:
#!/bin/bash
shopt -s extdebug
function stuff() {
local argIndex="${#BASH_ARGV[#]}"
while [[ argIndex -gt 0 ]] ; do
argIndex=$((argIndex - 1))
echo -n "${BASH_ARGV[$argIndex]} "
done
echo
}
stuff
I want to highlight:
The shopt -s extdebug is important. Without this the BASH_ARGV array will be empty unless you use it in top level part of the script (it means outside of the stuff function). Details here: Why does the variable BASH_ARGV have a different value in a function, depending on whether it is used before calling the function
BASH_ARGV is a stack so arguments are stored there in backward order. That's the reason why I decrement the index inside loop so we get arguments in the right order.
Double quotes around the ${BASH_ARGV[#]} and the # as an index instead of * are needed so arguments with spaces are handled properly. Details here: bash arrays - what is difference between ${#array_name[*]} and ${#array_name[#]}
You can use the shift keyword (operator?) to iterate through them.
Example:
#!/bin/bash
function print()
{
while [ $# -gt 0 ]
do
echo "$1"
shift 1
done
}
print "$#"
I do it like this:
#! /bin/bash
ORIGARGS="$#"
function init(){
ORIGOPT= "- $ORIGARGS -" # tacs are for sed -E
echo "$ORIGOPT"
}
The simplest and likely the best way to get arguments passed from the command line to a particular function is to include the arguments directly in the function call.
# first you define your function
function func_ImportantPrints() {
printf '%s\n' "$1"
printf '%s\n' "$2"
printf '%s\n' "$3"
}
# then when you make your function call you do this:
func_ImportantPrints "$#"
This is useful no matter if you are sending the arguments to main or some function like func_parseArguments (a function containing a case statement as seen in previous examples) or any function in the script.

Resources