Here's the script I am executing:
#!/bin/bash
...
function myprint() {
cp=$1
targetpermission=$2
t1="RESOURCE"
t2="Current Permission"
t3="New Permission"
printf "%s : %s ===> %s\n",$t1,$t2,$t3
}
myprint
Expected output:
RESOURCE : Current Permission ===> New Permission
Output I am getting:
[abhyas_app01#localhost safeshell]$ ./test1.sh
Permission,New : Permission ===>
,RESOURCE,Current[abhyas_app01#localhost safeshell]$
Just what's going on? How do I get the expected output?
PS - echo is not an option, because eventually I am going to justify the strings using %-Ns where N is a calculated based on terminal width.
In the shell, printf is a command and command arguments are separated by spaces, not commas.
Use:
printf "%s : %s ===> %s\n" "$t1" "$t2" "$t3"
The double quotes are necessary too; otherwise the Current Permission and New Permission arguments will be split up and you'll get two lines of output from your single printf command.
I note that your function takes (at least) two arguments, but you do not show those being printed or otherwise used within the function. You may need to revisit that part of your logic.
With your current logic:
t1="RESOURCE"
t2="Current Permission"
t3="New Permission"
printf "%s : %s ===> %s\n",$t1,$t2,$t3
you really pass to printf:
printf "%s : %s ===> %s\n,RESOURCE,Current" "Permission,New" "Permission"
Note how the 'format string' argument includes all of $t1 and part of $t2. You only provide two arguments for the three %s formats, so the third one is left empty. The output is:
Permission,New : Permission ===>
,RESOURCE,Current
with a space at the end of the first line and no newline at the end of the second 'line' (hence your prompt appears immediately after Current).
Related
I have two functions in Bash. One is a generic run function, that accepts an input and evaluates it, while printing the command, and testing the exit code. This is used in a large script to ensure each command executes successfully before continuing.
The second one is a complex function, that is doing some Git history parsing. The problematic line is the only one shown.
I am calling this function from a for-loop, that iterates over a list of terms to search. The issue is that spaces are not being handled correctly, when between other words. I have tried running my script though shell-checking websites, and all of the suggestions seem to break my code.
function run() {
echo "> ${1}"
eval "${1}"
# Test exit code of the eval, and exit if non-zero
}
function searchCommitContents() {
run 'result=$(git log -S'"${1}"' --format=format:%H)'
# Do something with result, which is a list of matching SHA1 hashes for the commits
echo "${result}"
}
# Main
declare -a searchContents=('foo' 'bar' ' foo ' 'foo bar')
for i in "${searchContents[#]}"
do
searchCommitContents "${i}"
done
Here is the output I get:
> result=$(git log -Sfoo --format=format:%H)
<results>
> result=$(git log -Sbar --format=format:%H)
<results>
> result=$(git log -S foo --format=format:%H)
<results>
> result=$(git log -Sfoo bar --format=format:%H)
fatal: ambiguous argument 'bar': unknown revision of path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'
I tried to add additional single and double-quotes to various areas of the code, such that the 'foo bar' string would not resolve to two different words. I also tried adding an escape to the dollar sign, like so: -s'"\${1}"' based on other questions on this site.
Why are you printing result=$(? It's an internal variable, it can be anything, there is no need for it in logs.
Print the command that you are executing, not the variable name.
run() {
echo "+ $*" >&2
"$#"
}
searchCommitContents() {
local result
result=$(run git log -s"${1}" --format=format:%H)
: do stuff to "${result}"
echo "$result"
}
issue with an input that has a space in the middle.
If you want quoted string, use printf "%q" or ${...#Q} for newer Bash, but I don't really enjoy both quoting methods and just use $*. I really like /bin/printf from GNU coreutils, but it's a separate process... while ${..#Q} is the fastest, it's (still) not enough portable for me (I have some old Bash around).
# compare
$ set -- a 'b c' d
$ echo "+ $*" >&2
+ a b c d
$ echo "+$(printf " %q" "$#")" >&2
+ a b\ \ c d
$ echo "+" "${##Q}" >&2
+ 'a' 'b c' 'd'
$ echo "+$(/bin/printf " %q" "$#")" >&2
+ a 'b c' d
See these lines:
> result=$(git log -Sfoo bar --format=format:%H)
fatal: ambiguous argument 'bar': unknown revision of path not in the working tree.
Specifically this: -Sfoo bar. It should be -S"foo bar" or -S "foo bar". Because to pass an argument with spaces, we need to quote the argument. But, each time the argument pass through a command/function layer, one layer of quote ('', "") is extracted. So, we need to nest the quote.
So in this line:
declare -a searchContents=('foo' 'bar' ' foo ' 'foo bar')
change 'foo bar' to '"foo bar"' or "'foo bar'" or "\"foo bar\"".
This is a case of 2 layers nested quotes. The more the layer, the trickier it gets. Here's an example of 4 layers quotes I once did.
Platform CentOS Linux release 7.6.1810, working in bash.
GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)
This is an idiom I've seen recommended for parsing text in bash in general and in particular for returning multiple values from a function.
IFS=":" read A B <<< $(echo ONE:TWO)
I'm getting unexpected behaviour when I call a function, yyy in the example here
IFS=":" read Y1 Y2 <<< $(yyy)
where yyy itself also wants to do a similar call.
The effect is that that within yyy() even though I explicitly specify the IFS
IFS=":" read C1 C2 <<< $( echo "A:B" )
The fields are parsed, but both values are assigned to C1, it gets the value "A B". If the function is called in isolation it works as expected.
This is a test case, distilled down from a much larger script. I want to know what is happening with IFS here. In the failure case (the second example below) setting IFS=":" in the caller somehow cause the result fields to be aggregated. The first and third calls to yyy() below work as expected, output shown after the code.
#!/bin/bash
debug() { echo "$1" 1>&2 ; }
yyy() {
debug "in yyy"
# why are the two values assigned to A here if the caller specified IFS?
IFS=":" read A B <<< $(echo ONE:TWO)
debug "A=$A"
debug "B=$B"
echo "$A:$B"
}
# this works as expected
read Y1 Y2 <<< $(yyy)
echo -e "===\n"
# this cause the read in yyy() to aggregate
IFS=":" read Y1 Y2 <<< $(yyy)
echo -e "===\n"
# This is a workaround that enables yyy() to work correctly
# But why do I need to do this?
OUT="$(yyy)"
IFS=":" read Y1 Y2 <<< $(echo $OUT)
This is the output
in yyy
A=ONE B=TWO
===
in yyy
A=ONE TWO B=
===
in yyy
A=ONE B=TWO
Note that in the second case A gets the value ONE TWO
This seems to be a bug in bash-4.2 as discussed here, IFS incorrectly splitting herestrings in bash 4.2. Should work on the versions above that.
These are the results on the same version as you have - GNU bash, version 4.2.46(2). When I ran the function yyy in debug mode ( by setting set -x in prompt ).
++ IFS=:
++ read A B
+++ echo ONE:TWO
++ debug 'A=ONE TWO'
++ echo 'A=ONE TWO'
A=ONE TWO
++ debug B=
++ echo B=
B=
++ echo 'ONE TWO:'
The above is snippet of the output from the debug mode output. As you can see when the echo ONE:TWO is printed as a result of the command substitution, no word splitting is expected to happen because the line doesn't contain any character of the default IFS value (space/tab or a newline)
So you would expect reading the the whole string with IFS=: expected to split the string and put the values in the constituent variables A and B, but somehow the : character is lost and a string ONE TWO is stored as the first variable value.
Look at the output of the function execution in GNU bash, version 4.4.12(1) which exhibits the right behavior.
++ IFS=:
++ read A B
+++ echo ONE:TWO
++ debug A=ONE
++ echo A=ONE
A=ONE
++ debug B=TWO
++ echo B=TWO
B=TWO
++ echo ONE:TWO
There have been lot of IFS related bugs up to version 4.4.0 bash/CHANGES. So a personal recommendation is to upgrade your bash version to a more recent stable one. Also see Trying to split a string into two variables
Similar bug on version 4.4.0(1)-release
You would expect the ONE:TWO to be unmodified when the $(..) is expanded because for reasons mentioned earlier. But here too the delimit character is lost and the variable A is set to ONE TWO
IFS=":" read A B <<< $(echo ONE:TWO)
echo "$A"
ONE TWO
Surprisingly the above code works on 4.2.46(2), which means the 4.4.0(1) broke a functionality which used to work in the earlier releases.
An argument is passed to bash script from outside and is read within bash file. Looks like as follows:
#following is the point from where the argument is passed to config.sh
controller.vm.provision :shell, path: 'shell/config.sh', keep_color: true, privileged: false, :args => ip
inside the config.sh reading the argument. The argument "ip" is somewhat like following:
ip = "10.12.153.26" "10.12.153.25" "10.12.153.24"
Now i want to iterate over the above argument inside the bash. so doing as follows:
array=($1) //please note $1="10.12.153.26" "10.12.153.25" "10.12.153.24"
for i in ${array[#]}
do
echo $i //it is iterated only once and output is "10.12.153.26" "10.12.153.25" "10.12.153.24"
done
So the output i am getting only once and is complete argument as it is but i want it to get displayed one by one so, that i can even use the single value separately for some other purpose. So, please suggest how can i have this?
If you want to split the value on spaces, you can do like this:
set -- $1
for i; do
echo $i
done
If the value of $i is "10.12.153.26" "10.12.153.25" "10.12.153.24",
then this will output:
"10.12.153.26"
"10.12.153.25"
"10.12.153.24"
To get rid of the double-quotes, you could use parameter expansion:
set -- $1
for i; do
echo ${i//\"/}
done
I wrote simple script as follow
#!/bin/bash
auth_type=""
SM_Read-only="Yes"
SM_write-only="No"
echo -e ${SM_Read-only}
echo -e ${SM_Write-only}
if [ "${SM_Read-only}" == "Yes" ] && [ "${SM_Write-only}" == "Yes" ]
then
auth_type="Read Write"
else
auth_type="Read"
fi
echo -e $auth_type
And when i execute it i got following output with errors.
./script.bash: line 5: SM_Read-only=Yes: command not found
./script.bash: line 6: SM_write-only=No: command not found
only
only
Read
Any one know correct way to declare the variable with "-" (dash)?
EDIT:
have getting response from c code and evaluate the variables for example
RESP=`getValue SM_ Read-only ,Write-only 2>${ERR_DEV}`
RC=$?
eval "$RESP"
from above scripts code my c binary getValue know that script want Read-only and Write-only and return value to script.So during eval $RESP in cause error and in my script i access variable by
echo -e ${SM_Read-only}
echo -e ${SM_Write-only}
which also cause error.
Rename the variable name as follows:
SM_Read_only="Yes"
SM_write_only="No"
Please, don't use - minus sign in variable names in bash, please refer to the answer, on how to set the proper variable name in bash.
However if you generate the code, based on others output, you can simply process their output with sed:
RESP=$(getValue SM_ Read-rule,Write-rule 2>${ERR_DEV}|sed "s/-/_/g")
RC=$?
eval "$RESP"
- is not allowed in shell variable names. Only letters, numbers, and underscore, and the first character must be a letter or underscore.
I think you cant have a dash in your variables names, only letters, digits and "_"
Try:
SM_Read_only
Or
SM_ReadOnly
I'm attempting to write a KornShell (ksh) function that uses printf to pad a string to a certain width.
Examples:
Call
padSpaces Hello 10
Output
'Hello '
I currently have:
padSpaces(){
WIDTH=$2
FORMAT="%-${WIDTH}.${WIDTH}s"
printf $FORMAT $1
}
Edit: This seems to be working, in and of itself, but when I assign this in the script it seems to lose all but the first space.
TEXT=`padSpaces "TEST" 10`
TEXT="${TEXT}A"
echo ${TEXT}
Output:
TEST A
I'm also open to suggestions that don't use printf. What I'm really trying to get at is a way to make a fixed width file from ksh.
Your function works fine for me. Your assignment won't work with spaces around the equal sign. It should be:
SOME_STRING=$(padSpaces TEST 10)
I took the liberty of replacing the backticks, too.
You don't show how you are using the variable or how you obtain the output you showed. However, your problem may be that you need to quote your variables. Here's a demonstration:
$ SOME_STRING=$(padSpaces TEST 10)
$ sq=\'
$ echo $sq$SOME_STRING$sq
'TEST '
$ echo "$sq$SOME_STRING$sq"
'TEST '
Are you aware that you define a function called padSpaces, yet call one named padString? Anyway, try this:
padString() {
WIDTH=$2
FORMAT="%-${WIDTH}s"
printf $FORMAT $1
}
Or, the more compact:
padString() {
printf "%-${2}s" $1
}
The minus sign tells printf to left align (instead of the default right alignment). As the manpage states about the command printf format [ arg ... ],
The arguments arg are printed on standard output in accordance with the
ANSI-C formatting rules associated with the format string format.
(I just installed ksh to test this code; it works on my machineTM.)