How to modify command line arguments inside bash script using set - linux

I'm executing a shell script and passing few command line arguments to it.
I want to modify the arguments inside the script using set. Not all at once depending upon some conditions.
How can I do that?

Copy unmodified arguments at their respective location within set --
Say you want to modify value of argument 2:
set -- "${#::2}" 'new arg2 value' "${#:3}"
Explanation:
"${#::2}": Expands 2 arguments from index 0 (arguments 0 and 1)
new arg2 value: Becomes the value for argument 2.
"${#:3}": Expands all argument values starting at index 3.
Opinion:
Anyway, having mutable arguments is considered code-smell in modern programming. So I'd recommend you reconsider your approach to the problem you are trying to solve.

Related

Bash - Get the VALUE of 'nested' variable into another variable [Edit: Indirect Variable Expansion] [duplicate]

This question already has answers here:
Bash - variable variables [duplicate]
(4 answers)
Lookup shell variables by name, indirectly [duplicate]
(5 answers)
How to use a variable's value as another variable's name in bash [duplicate]
(6 answers)
Is it possible to build variable names from other variables in bash? [duplicate]
(7 answers)
Closed 5 years ago.
I'm trying to get get the VALUE of a 'nested' variable into another variable and/or use the value directly as shown below
Below is an example scenario which exactly explains where I'm stuck
$ USER1_DIR=./user1/stuff
$ USER2_DIR=./user2/stuff
$ USER3_DIR=./user3/stuff
#User will be taken as input, for now assuming user is USER1
$ USER="USER1"
$ DIR=${USER}_DIR
$ echo $DIR
>> USER1_DIR
$ DIR=${${USER}_DIR}
>> -bash: ${${USER}_DIR}: bad substitution
Challenge 1:
Get DIR value to ./user1/stuff when the input is USER1
or
Get ./user1/stuff as output when the input is USER1
After I'm able to get through Challenge 1, I've to add some content to a file in the user directory like below
Desired output is as below
$ echo "Some stuff of user1" >> $DIR/${DOC}$NO
# Lets say DOC="DOC1" and NO="-346"
# So the content has to be added to ./user1/stuff/DOC1-346
# Assume that all Directories exists
FYI, The above code will be a part of a function in a bash script and it will be executed only on a Linux server.
Note : I don't know what to call variable DIR hence used the term 'nested' variable. It would be great to know what is it called, greatly appreciate any insight. :)
You can use eval, variable indirection ${!...}, or reference variables declare -n.
In the following, I will use lowercase variable names, since uppercase variable names are special by convention. Especially overwriting $USER is bad, because that variable normally contains your user name (without explicitly setting it). For the following code fragments assume the following variables:
user1_dir=./user1/stuff
user=user1
Eval
eval "echo \${${user}_dir}"
# prints `./user1/stuff`
Eval is a bash built-in that executes its arguments as if they were entered in bash itself. Here, eval is called with the argument echo "${user1_dir}".
Using eval is considered bad practice, see this question.
Variable Indirection
When storing the name of variable var1 inside another variable var2, you can use the indirection ${!var2} to get the value of var1.
userdir="${user}_dir"
echo "${!userdir}"
# prints `./user1/stuff`
Reference Variables
Instead of using indirection every time, you also can declare a reference variable in bash:
declare -n myref="${user}_dir"
The reference can be used similar to variable indirection, but without having to write the !.
echo "$myref"
# prints `./user1/stuff`
Alternatives
Your script may become easier when using (associative) arrays. Arrays are variables that store multiple values. Single values can be accessed by using an index. Normal arrays use natural numbers as indices. Associative arrays use arbitrary strings as indices.
(Normal) Arrays
# Create an array with three entries
myarray=(./user1/stuff ./user2/stuff ./user3/stuff)
# Get the first entry
echo "${myarray[0]}"
# Get the *n*-th entry
n=2
echo "${myarray[$n]}"
Associative Arrays
Declare an associative array with three entries
# Create an associative array with three entries
declare -A myarray
myarray[user1]=./user1/stuff
myarray[user2]=./user2/stuff
myarray[user3]=./user3/stuff
# Get a fixed entry
echo "${myarray[user1]}"
# Get a variable entry
user=user1
echo "${myarray[$user]}"

In BASH, can we assign and display the value in the variable _ (underscore)?

Answering to the following question:
Allowed characters in linux environment variable names #aiden-bell writes that the following patterns gives all allowed shell variable names in BASH : [a-zA-Z_]+[a-zA-Z0-9_]*
I found this to be true. In fact I can export value _="Just for fun". Unfortunately though, whenever I print it I get __bp_preexec_invoke_exec
I went through this thread and while it is instructive it doesn't actually answer my question. Irrespective of whatever the shell might do with the variable $_, can I use it for my own means? Also, whatever exactly is __bp_preexec_invoke_exec? Thanks and regards.
You can assign to the special parameter _, but the shell will also update its value after each command. Typically, you only use it as a dummy variable where you don't care about the result, such as in something like
while read _ second _ ; do ...; done < input.txt
where you only care about the second column of each input line.
From the man page:
_ At shell startup, set to the absolute pathname used to invoke
the shell or shell script being executed as passed in the envi-
ronment or argument list. Subsequently, expands to the last
argument to the previous command, after expansion. Also set to
the full pathname used to invoke each command executed and
placed in the environment exported to that command. When check-
ing mail, this parameter holds the name of the mail file cur-
rently being checked.

cant create third parameter in bash script (after $*)

I want to create a bash script, that gets 3 parameters. But the second needs to be $*, because i need later these lines. The other two parameters (first and third) doesn't need this.
for x in $* do
The first and second parameter aren't the problem, this one works:
parameter1="$1"
shift
parameter2="$*"
But i need the third parameter at the end and something like this
parameter1="$1"
parameter3="$3"
shift
parameter2="$*"
won't work. My command at the end should look like this:
bash myscript parameter1 parameter2 parameter3
For specifically three parameters, you can use substring parameter expansion in a simple way:
parameter1=$1
parameter2="${#:2:1}" # One parameter, starting with #2
parameter3=$3
Or course, that's unnecessary, since you can just use $2 instead of ${#:2:1}, but I point it out as a simple introduction to the syntax (and not at all because I overlooked the fact you would use $2, really....)
(You can also use it as a substitute for indirect parameter expansion; "${#:n:1}" and "${!n}" are basically equivalent when n is a variable with an integer value.)
For the more general case, where you want an arbitrary number of arguments between the first and last, it gets a little more complicated, although the principle is the same:
parameter1=$1
middleParameters=( "${#:2:$#-2}" ) # n - 2 parameters, starting with #2, i.e., all but $1 and ${!n} for n=$#
lastParameter="${#:$#}"
shift removes an argument from the left. If you want to remove an argument from the right, you can do that with:
set -- "${#:1:$# - 1}"
Thus:
parameter1=$1 # capture leftmost argument
shift # remove leftmost argument
parameter3=${*:$#:1} # capture rightmost argument
set -- "${#:1:$# - 1}" # remove rightmost argument
parameter2=$* # concatenate remaining arguments and store in a string
Note that $* is almost certainly the Wrong Thing. If you want to keep your arguments separate, respecting their quoting, instead use an array:
parameter2=( "$#" )
for item in "${parameter2[#]}"; do
echo "Processing item: $item"
done
If your script is run as yourscript arg1 "item A" "item B" arg3, then the above will ensure that item A and item B are treated as individual arguments, rather than treating item as an argument, A as another, etc.

what does this notation in hive script(hivequery.hql) file mean "use ${word:word}"

The script (hivequery.hql:) looks like this:
Use ${platformType:platformName};
select * from hivetablename;
And this script is being called in a bash script as
#!/usr/bin/env bash
hive -f hivequery.hql
Within an hql file, the use command sets the default database. See Use Database.
The ${platformType:platformName} is Hive's variable notation where platformType is the namespace and platformName is the variable name. This is explained in the Using Variables section of the Language Manual.
If you want to see what value a specific variable has, you can just use set like:
set platformType:platformName;
and it will print out the value. You can also run set; to get a full listing of known variables in all namespaces.
The more correct way to write the construct ${word:word} would be to write ${parameter:offset} . It cause parameter expansion, it expands to the portion of the value of parameter starting at the character (counting from 0 ) determined by expanding offset to the end of the parameter . It has one more variant as ${parameter:offset:length } - Expands to the portion of the value of parameter starting at the character (counting from 0 ) determined by expanding offset as an arithmetic expression and consisting of the number of characters determined by the arithmetic expression defined by length.
So I think basically the in your case , it is meant to get the name of the database from platformType.
For more details on this look into the
Look for Parameter Expansion in the bash man page.

Passing List of Variables In Bash To External Program

Good Afternoon Everyone,
This is probably a no-brainer but, I'm currently having issues passing a variable to a program in my bash script.
Here's what I'm trying to do:
regions=ne,se,vt,ma,sw,nw and so on and so forth
After that variable has been defined in my bash script, I'd then like to pass that variable into GrADS, where my script will read each of the regions one after the other until it reaches the end.
The most reliable means of passing variables I've found is to generate a text file with the code (or just the string) you want to pass from within the code. Alternatively, you could call GrADS (?) from within whatever program is generating the variable, and pass "$regions" as an argument.

Resources