bash command output as parameters - bash

Assume that the command alpha produces this output:
a b c
d
If I run the command
beta $(alpha)
then beta will be executed with four parameters, "a", "b", "c" and "d".
But if I run the command
beta "$(alpha)"
then beta will be executed with one parameter, "a b c d".
What should I write in order to execute beta with two parameters, "a b c" and "d". That is, how do I force $(alpha) to return one parameter per output line from alpha?

You can use:
$ alpha | xargs -d "\n" beta

Similar to anubhava's answer, if you are using bash 4 or later.
readarray -t args < <(alpha)
beta "${args[#]}"

Do that in 2 steps in bash:
IFS=$'\n' read -d '' a b < <(alpha)
beta "$a" "$b"
Example:
# set IFS to \n with -d'' to read 2 values in a and b
IFS=$'\n' read -d '' a b < <(echo $'a b c\nd')
# check a and b
declare -p a b
declare -- a="a b c"
declare -- b="d"

Script beta.sh should fix your issue:
$ cat alpha.sh
#! /bin/sh
echo -e "a b c\nd"
$ cat beta.sh
#! /bin/sh
OIFS="$IFS"
IFS=$'\n'
for i in $(./alpha.sh); do
echo $i
done

Related

How to loop a nested loop in bash without repetition?

I am trying to write two loops that would make a function together as I require both variables in one deployment for azure such as a name of storage account and container name to contain their key and store it but I am getting repeated results.
for storage in $(cat $TMP_FILE_STORAGE | sed 's/^[^"]*"\([^"]*\)".*/\1/' )
do
echo $storage
for container in $(cat $TMP_FILE_CONTAINER| sed 's/^[^"]*"\([^"]*\)".*/\1/' )
do
echo $container
continue
done
done
This is the file for container json :
lama baba
This is the file for storage json :
abdelvt33cpgsa abdelvt44cpgsa
This is the output I am getting
abdelvt33cpgsa
lama
baba
abdelvt44cpgsa
lama
baba
and the expected result should be
abdelvt33cpgsa
lama
abdelvt44cpgsa
baba
See Bash FAQ 001; you shouldn't be using for loops in the first place.
Instead, use a while loop with two separate file descriptors.
while IFS= read -r storage <&3 && IFS= read -r container <&4 ; do
echo "$storage"
echo "$container"
done 3< <(sed 's/^[^"]*"\([^"]*\)".*/\1/' < "$TMP_FILE_STORAGE" ) 4< <(sed 's/^[^"]*"\([^"]*\)".*/\1/' < "$TMP_FILE_CONTAINER")
It appears you might be able to get rid of sed as well by splitting each input line on ":
while IFS=\" read -r _ storage _ <&3 &&
IFS=\" read -r _ container _ <&4; do
echo "$storage"
echo "$container"
done 3< "$TMP_FILE_STORAGE" 4< "$TMP_FILE_CONTAINER"
Assuming
your files don't contain tab characters, and
they have the same number of lines,
a quick way is to paste both files and then convert the separator character (tab by default) to newlines:
$ cat A-file
A
B
C
D D
E E
$ cat 1-file
1
2
3
4 4
5 5
$ paste A-file 1-file|tr '\t' '\n'
A
1
B
2
C
3
D D
4 4
E E
5 5
Look how Stackoverflow syntax coloring makes this example look cool!
The right solution to this was to run it over aray loop.
Potato=$(cat $TMP_FILE_STORAGE)
Potato1=$(cat $TMP_FILE_CONTAINER)
eval "array=($Potato)"
eval "array2=($Potato1)"
for ((i=0; i<${#array[#]}; i++)); do
end=`date -d "15 minutes" '+%Y-%m-%dT%H:%MZ'`
az storage container generate-sas -n ${array2[$i]} --account-name ${array[$i]} --https-only --permissions dlrw --expiry $end -otsv`
echo "First parameter : ${array[$i]} -- second parameter : ${array2[$i]}"
done

How to make two variables from one positional parameter?

I need to add two numbers to a positional argument $1 with a dash between them i.e. "15-20". It is an interval from 15 to 20 and it has to be a one positional parameter. I did this:
#!/bin/sh
a=$(echo "$1" | sed 's/-/ /g')
echo $a
It prints:
15 20
I will need this in the future as an interval so I have to take each of these numbers as a seperate variables. Instead of a = 15 20, it should be a = 15 and b = 20. How can I achieve this?
With /bin/sh, use read and a here document.
IFS=- read a b <<EOF
$1
EOF
Or, use parameter expansion twice to drop the prefix/suffix.
a=${1%-*}
b=${1#*-}
Using bash you can use process substitution:
read a b < <(echo "$1" | sed 's/-/ /g')
# and check values
declare -p a b
declare -- a="15"
declare -- b="20"
As the helpful comment from #chepner below, you don't even need sed. You can use read both variables using a custom IFS:
IFS=- read a b <<< "$1"

why read command reads all words into first name

My script:
#!/bin/bash
IFS=','
read a b c d e f g <<< $(echo "1,2,3,4,5,6,7") # <- this could be any other commands, I am just making up a dummy command call
echo $a
echo $b
echo $c
I expected it to output
1
2
3
But instead it outputs:
1 2 3 4 5 6 7
blank line
blank line
What did I do wrong?
You should use it like this:
IFS=, read a b c d e f g <<< "1,2,3,4,5,6,7"
Use IFS in same line as read to avoid cluttering the current shell environment.
And avoid using command substitution just to capture the output of a single echo command.
If you want to use a command's output in read then better use process substitution in bash:
IFS=, read a b c d e f g < <(echo "1,2,3,4,5,6,7")
This works:
#!/bin/bash
IFS=','
read a b c d e f g <<< "$(echo "1,2,3,4,5,6,7")"
echo $a; echo $b; echo $c
Note the quoting: "$( ...)". Without it, the string is split and becomes
$(echo "1,2,3,4,5,6,7") ===> 1 2 3 4 5 6 7
Giving 1 2 3 4 5 6 7 to read produces no splitting, as the IFS is ,.
Of course, this also works (IFS only apply to the executed command: read):
#!/bin/bash
IFS=',' read a b c d e f g <<< "$(echo "1,2,3,4,5,6,7")"
echo $a; echo $b; echo $c
And is even better like this:
#!/bin/bash
IFS=',' read a b c d e f g <<< "1,2,3,4,5,6,7"
echo $a; echo $b; echo $c
You do not need to "execute an echo" to get a variable, you already have it.
Technically, your code is correct. There is a bug in here-string handling in bash 4.3 and earlier that incorrectly applies word-splitting to the unquoted expansion of the command substitution. The following would work around the bug:
# Quote the expansion to prevent bash from splitting the expansion
# to 1 2 3 4 5 6 7
$ read a b c d e f g <<< "$(echo "1,2,3,4,5,6,7")"
as would
# A regular string is not split
$ read a b c d e f g <<< 1,2,3,4,5,6,7
In bash 4.4, this seems to be fixed:
$ echo $BASH_VERSION
4.4.0(1)-beta
$ IFS=,
$ read a b c d e f g <<< $(echo "1,2,3,4,5,6,7")
$ echo $a
1

how to concatenate all inputs including whitepaces to one string?

for a script
script.sh < a b c a
I want to put the inputs in one string, like that
string=" a b c a"
ie, preserve the spaces
how can i do that?
thanks
Consider using here documents and here strings.
script.sh:
#!/bin/bash
readarray -t INPUT
printf "input: %s\n" "${INPUT[#]}"
example:
bash script.sh <<< "a b c"

Unset IFS value for shell script

In my use case I would like to change the value of IFS to a known separator (-). I tried the following:
OLDIFS=$IFS
IFS='-'
for x in $*
do
echo $x
done
IFS=$OLDIFS
When using e.g. -a b -c d as input string I expect the output to be
a b
c d
However, what I get is
a
b
c
d
I'm on AIX.
I tried your code and I get
a b
c d
Try this
$ cat >a <<.
#!/bin/sh
OLDIFS=$IFS
IFS='-'
for x in $*
do
echo $x
done
IFS=$OLDIFS
.
$ chmod +x a
$ ./a "-a b -c d"
a b
c d
$
Here is one way of getting this output using awk and avoid all the IFS manipulation:
s='-a b -c d'
echo "$s" | awk -F ' *- *' '{print $2 RS $3}'
a b
c d

Resources