Array to strings of index value pairs - arrays

I would like to create a string of index and value pairs. This is to be used in dialog menu.
declare -r -a arr=(
piano
table
chair
)
dialog \
--backtitle "Launcher" \
--title "App" \
--ok-label 'Select' \
--cancel-label "Back" \
--menu "Select an application" \
$dialog_height $dialog_width 4 \
"1" "piano" \
"2" "table" \
"3" "chair"
So I'm looking to have the:
"1" "piano" \
"2" "table" \
"3" "chair"
generated automatically from array.
There's a similar question pass an array to dialog menu I tried what's there I couldn't get it to work for me.

To auto-generate the tags 1, 2, 3 in front of every array entry, use
items=(piano table chair)
taggedItems=()
for ((i=1; i<="${#items[#]}"; ++i)); do
taggedItems+=("$i" "${items[i-1]}")
done
# then use "${taggedItems[#]}"
Opposed to your linked awk solution, this works with arbitrary items that may contain spaces, special symbols like * and even linebreaks.

declare -a arr=(
''
piano
table
chair
)
unset 'arr[0]'
declare -ar arr
# Remove the echo below once ready for the fun.
echo dialog \
--backtitle "Launcher" \
--title "App" \
--ok-label 'Select' \
--cancel-label "Back" \
--menu "Select an application" \
$dialog_height $dialog_width 4 \
$(for i in "${!arr[#]}"; do echo "$i" "${arr[i]}"; done)

OPTIONS=(1 "table"
2 "piano"
3 "......")
dialog \
--backtitle "Launcher" \
--title "App" \
--ok-label 'Select' \
--cancel-label "Back" \
--menu "Select an application" \
$dialog_height $dialog_width 4 \
"${OPTIONS[#]}" \
2>&1 >/dev/tty

There are two ways, using simple array:
#!/bin/bash
declare -r -a arr=(
piano
table
chair
)
options=$(printf '%s\n' "${arr[#]}" | awk '{print v++,$arr}')
dialog \
--backtitle "Test" \
--title "Test" \
--menu "Test" \
0 0 4 \
$options \
2>&1 >/dev/tty
Or with associative array:
#!/bin/bash
declare -r -A arr=(
piano
table
chair
)
options=$(printf '%s\n' "${!arr[#]}" | awk '{print v++,$arr}')
dialog \
--backtitle "Test" \
--title "Test" \
--menu "Test" \
0 0 4 \
$options \
2>&1 >/dev/tty
To start menu numbers at 1:
#!/bin/bash
declare -r -a arr=(
piano
table
chair
)
options=$(printf '%s\n' "${arr[#]}" | awk -v v=1 '{print v++,$arr}')
dialog \
--backtitle "Test" \
--title "Test" \
--menu "Test" \
0 0 4 \
$options \
2>&1 >/dev/tty
To use both key and value of associative array in menu:
#!/bin/bash
declare -r -A arr=(
[piano]=piano
[table]=mesa
[chair]=cadeira
)
options=$(printf "%s\n" "${!arr[#]} " "${arr[#]}")
dialog \
--backtitle "Test" \
--title "Test" \
--menu "Test" \
0 0 4 \
$options \
2>&1 >/dev/tty

Correct answer works both with normal and associative arrays of entries, using numerical index or associative key as menu key:
Normal array of menu entries, using numerical index as menu key:
#!/usr/bin/env bash
arr=(
piano
table
chair
)
options=()
for k in "${!arr[#]}"; do
options+=( "$k" "${arr[$k]}" )
done
dialog \
--backtitle "Test" \
--title "Test" \
--menu "Test" \
0 0 4 \
"${options[#]}" \
>/dev/tty 2>&1
Full example with associative array:
#!/usr/bin/env bash
declare -A assoc=(
[p]=piano
[t]=table
[c]=chair
)
options=()
for k in "${!assoc[#]}"; do
options+=("$k" "${assoc[$k]}")
done
choice="$(
dialog \
--backtitle "Test" \
--title "Test" \
--menu "Test" \
0 0 4 \
"${options[#]}" \
2>&1 >/dev/tty
)"
clear
case $choice in
t)
printf %s\\n "Let's set the table."
;;
p)
printf %s\\n "Let's play piano."
;;
c)
printf %s\\n "Let's sit dow."
;;
*)
printf %s\\n "Let's decide later."
;;
esac

Related

Is X11 required for adding text overlay?

I'm trying to run the below command in a script and it seems it's not adding any text layout to the video, I'm sure I didn't it before and it was fine.
My question is, do I need to set up X11 environment in order to use dynamictext or text filters?
Thanks in advance.
/usr/bin/melt \
"/var/www/html/test/nkLcBPkebo/t-1.mp4" \
-audio-track \
"/var/www/html/test/nkLcBPkebo/t-1_sound.mp4" \
-attach-track \
"text:This is my best video" \
-0 \
in=0 out=0 fgcolour="#004fed" bgcolour=0 olcolour="#fff200" outline=3 pad="50x0" size=80 weight=700 style="italic" halign="center" valign="top" family="Ubuntu" \
-profile hdv_720_25p -progress \
-consumer avformat:"/var/www/html/test/nkLcBPkebo/1.mp4" \
vcodec="libx264" vb="5000k" acodec="aac" ab="128k" frequency=44100 deinterlace=1
I think I found the issue! it's was all about the order of the parameters :)
Having the text right after the video (before the audio) should fix it
/usr/bin/melt \
"/var/www/html/test/nkLcBPkebo/t-1.mp4" \
-attach-track \
"text:This is my best video" \
-0 \
in=0 out=0 fgcolour="#004fed" bgcolour=0 olcolour="#fff200" outline=3 pad="50x0" size=80 weight=700 style="italic" halign="center" valign="top" family="Ubuntu" \
-audio-track \
"/var/www/html/test/nkLcBPkebo/t-1_sound.mp4" \
-profile hdv_720_25p -progress \
-consumer avformat:"/var/www/html/test/nkLcBPkebo/1.mp4" \
vcodec="libx264" vb="5000k" acodec="aac" ab="128k" frequency=44100 deinterlace=1

SC2207 Bash array assignment from subshell not splitting as expected

I have been populating an array using:
AWS_STS_CREDS=( $(aws sts ...) )
This raises shellcheck error SC2207
Prefer mapfile or read -a to split command output
But the recommendation does not work as expected.
IFS=" " read -r -a AWS_STS_CREDS <<< "$(
aws sts assume-role \
--role-arn ${AWS_ROLE_ARN} --role-session-name ${AWS_SESSION_NAME} \
--query '[Credentials.AccessKeyId,Credentials.SecretAccessKey,Credentials.SessionToken]' \
--output text
)"
echo "Array contains ${#AWS_STS_CREDS[#]} elements"
#> Array contains 1 elements
echo "${AWS_STS_CREDS[0]}"
#> ASIAV2R3U... 4gXdep/GN... FwoGZXI...
I have also tried removing the quotes around the subcommand.
At first it appears as though the set IFS is having no effect but the below works:
IFS=" " read -r -a AWS_STS_CREDS <<< "$(echo '1 2 3')"
I am overlooking something but I'm having trouble identifying the problem and would like to understand the behaviour.
As per #Cyrus's suggestion, piping the subcommand output to cat -A clearly shows it is tab delimited. Indicated by ^I
echo "${AWS_STS_CREDS[0]}"
#> ASIAV2R3U...^I4gXdep/GN...^IFwoGZXI...
Amending the script as follows works as expected:
IFS=$'\t' read -r -a AWS_STS_CREDS <<< "$(
aws sts assume-role \
--role-arn ${AWS_ROLE_ARN} --role-session-name ${AWS_SESSION_NAME} \
--query '[Credentials.AccessKeyId,Credentials.SecretAccessKey,Credentials.SessionToken]' \
--output text
)"
echo "Array contains ${#AWS_STS_CREDS[#]} elements"
#> Array contains 3 elements
echo "${AWS_STS_CREDS[0]}"
#> ASIAV2R3U...

Refining Bash loop

Hello Stackoverflow community,
Please forgive me for my naïveté, but I have a basic loop script running a more complex script that is looking to a text file for the inputs that I have as an array. I have it working, I guess, but I know this can be ran better and more automated.
Here is the text file my script looks to;
2014;14204;
2015;15042;
2015;15062;
...
end;
Here is the bash script I run as a loop;
{ while IFS=';' read YEAR1 PROJ1 YEAR2 PROJ2 YEAR3 PROJ3 fake
do
{ echo "$YEAR1" | egrep '^#|^ *$' ; } > /dev/null && continue
$local/script.sh \
--forinput1 $YEAR1/$PROJ1 \
--forinput2 $YEAR2/$PROJ2 \
--forinput3 $YEAR3/$PROJ3 \
done
} < textFile.txt
I have done some research myself and have found somethings I thought would work but haven't been able to properly implement into this. If you could some me some pointers I would appreciate it.
Edited:
I do apologize, so the script does recognize the text file as such:
YEAR1;PROJ1;
YEAR2;PROJ2;
YEAR3;PROJ3;
Using the ";" as its separator. It does run in a loop until the last variable it done. However, for it function I need to add to the text file extra lines
YEAR4;PROJ4;
YEAR5;PROJ5;
end;
then add the script
{ while IFS=';' read YEAR1 PROJ1 YEAR2 PROJ2 YEAR3 PROJ3 YEAR4 PROJ4 YEAR5 PROJ5 fake
do
{ echo "$YEAR1" | egrep '^#|^ *$' ; } > /dev/null && continue
$local/script.sh \
--forinput1 $YEAR1/$PROJ1 \
--forinput2 $YEAR2/$PROJ2 \
--forinput3 $YEAR3/$PROJ3 \
--forinput4 $YEAR4/$PROJ4 \
--forinput5 $YEAR5/$PROJ5 \
done
} < textFile.txt
What I am hoping to accomplish is adding the variables in the array but not having to add the extra syntax into the script
This is broken but I guess what I am looking along the lines of
{ while IFS=';' read -a YEAR PROJ < textFile.txt
for ((i = 0; i < "${#YEAR[#]};${#PROJ[#]}"; i++)); do
{ echo "$YEAR[$i]" | egrep '^#|^ *$' ; } > /dev/null && continue
$local/script.sh \
--forinput[$i] ${YEAR[$i]}/${PROJ[$i]} \
done
}
Your code suggests that each inputline has 6 fields, what might lead to code like
sed -nr 's#([^;]*);([^;]*);([^;]*);([^;]*);([^;]*);([^;]*);#$local/script.sh --forinput \1/\2 --forinput \3/\4 --forinput \5/\6#p' textFile.txt
# or shorter
f='([^;]*;)'
sed -nr 's#'$f$f$f$f$f$f'#$local/script.sh --forinput \1/\2 --forinput \3/\4 --forinput \5/\6#p' textFile.txt
When you have to combine 3 lines for the input, you should not try to be smart.
# Don't do this
cat textFile.txt | paste -d'X' - - - | tr -d 'X'
# Trying to make
sed -nr 's#'$f$f$f$f$f$f'#$local/script.sh --forinput \1/\2 --forinput \3/\4 --forinput \5/\6#p' <(cat textFile.txt | paste -d'X' - - - | tr -d 'X')
After proudly presenting the code, you see you will have to make the code even worse, when the second line is a comment (starts with '#'). You need to replace cat textFile.txt with grep -Ev '^#|^ *$' testFile.txt.
When you need to relate different lines, take a look at awk.
The solution without checks is
awk -F';' '{line++}
{param=param " --forinput " $1 "/" $2}
line==3 {print "$local/script.sh" param ; param=""; line=0}
' textFile.txt
You can add all kind of checks.
I'm going to assume your input file contains groups of 3 lines each with 2 fields, not lines of 6 fields.
$ cat file
y1a;p1a;
y2a;p2a;
y3a;p3a;
y1b;p1b;
y2b;p2b;
y3b;p3b;
Then, you can put multiple read commands as the while "condition":
while
IFS=';' read year1 proj1 x
IFS=';' read year2 proj2 x
IFS=';' read year3 proj3 x
do
echo script \
--forinput1 "$year1/$proj1" \
--forinput2 "$year2/$proj2" \
--forinput3 "$year3/$proj3"
done < file
script --forinput1 y1a/p1a --forinput2 y2a/p2a --forinput3 y3a/p3a
script --forinput1 y1b/p1b --forinput2 y2b/p2b --forinput3 y3b/p3b
However, this does not handle comments and blank lines. This version executes the script after the 3rd non-(comment/blank) line
$ cat file
# set 1
y1a;p1a;
y2a;p2a;
y3a;p3a;
# set 2
y1b;p1b;
y2b;p2b;
y3b;p3b;
and
n=0
args=()
while IFS=';' read year project x; do
{ [[ $year == *([[:blank:]])"#"* ]] || [[ $year == *([[:blank:]]) ]]; } && continue
((n++))
args+=( "--forinput$n" "$year/$project" )
if (( n == 3 )); then
echo script "${args[#]}"
args=()
n=0
fi
done < file
script --forinput1 y1a/p1a --forinput2 y2a/p2a --forinput3 y3a/p3a
script --forinput1 y1b/p1b --forinput2 y2b/p2b --forinput3 y3b/p3b
Another approach to handle arbitrary records per group:
$ cat file
# set 1
y1a;p1a;
y2a;p2a;
y3a;p3a;
y4a;p4a;
y5a;p5a;
end;
# set 2
y1b;p1b;
y2b;p2b;
y3b;p3b;
end;
$ grep -Pv '^\s*(#|$)' file | awk -F"\n" -v RS="\nend;\n" -v OFS=, '{
cmd = "script.sh"
for (i=1; i<=NF; i++) {
n = split($i, a, /;/)
cmd = sprintf( "%s --forinput%d \"%s/%s\"", cmd, i, a[1], a[2])
}
print cmd
}'
script.sh --forinput1 "y1a/p1a" --forinput2 "y2a/p2a" --forinput3 "y3a/p3a" --forinput4 "y4a/p4a" --forinput5 "y5a/p5a"
script.sh --forinput1 "y1b/p1b" --forinput2 "y2b/p2b" --forinput3 "y3b/p3b"
That uses grep to filter out the comments and blank lines. Then awk comes it to format the commands:
RS is the record separator, using the end; line
-F"\n" sets the field separator to a newline
then we iterate over the number of fields (NF) to construct the command you want to run, and print it out.
To actually execute it, pipe the awk output to | sh

Nested array inside variable in for loop

I'm beginning in Bash and I try to do this, but I can't.
Example:
array=("/dev/sda1" "/dev/sdb1")
for i in "${array[#]";do
space=$(df -H | grep ${array[1]})
done
or this:
i=0
for i in ("/dev/sda1" "/dev/sdb1")
space=$(df -h | grep (($i++)))
done
Is this possible?
You can directly feed the array into df like below and avoid using for-loop like
df -H "${array[#]}"
However, if this is a venture to study the for-loop in bash then you can do the same like below
for i in "${array[#]}"
do
df -H "$i"
# Well, "${array[#]}" expands to everything in array
# and i takes each value in each turn.
done
And if you wish to access the array using index, then
for ((i = 0; i < ${#array[#]}; i++)) # This is a c-style for loop
do
df -H "${array[i]}" # Note the index i is not prefixed with $
done
Edit
To check if the usage is more than, say, 10GB
# We will usage file which we would use to fill the body of mail
# This file will contain the file system usage
# But first make sure the file is empty before every run
cat /dev/null > /tmp/diskusagereport
for i in "${array[#]}"
do
usage=$(df -H "$i" | awk 'END{sub(/G$/,"",$3);print $3}')
if [ "${usage:-0}" -gt 10 ]
then
echo "Filesystem : ${i} usage : ${usage}GB is greater than 10GB" >> /tmp/diskusagereport
fi
done
#finally use the `mailx` client like below
mailx -v -r "from#address.in" -s "Disk usage report" \
-S smtp="smtp.yourserver.org:port" -S smtp-auth=login \
-S smtp-auth-user="your_auth_user_here" \
-S smtp-auth-password='your_auth_password' \
to#address.com </tmp/diskusagereport

variable expansion as a pattern in sed not working

I've a simple script to set several parameters in /etc/ssh/sshd_config :
#! /bin/bash
declare -a param=('Banner' 'ClientAliveInterval' 'ClientAliveCountMax' 'Ciphers' \
'PermitUserEnvironment' 'PermitEmptyPasswords' 'PermitRootLogin' \
'HostbasedAuthentication' 'IgnoreRhosts' 'MaxAuthTries' \
'X11Forwarding' 'LogLevel'\
)
declare -a val=('/etc/issue.net' '300' '0' 'aes128-ctr,aes192-ctr,aes256-ctr' \
'no' 'no' 'no' 'no' 'yes' '4' 'no' 'INFO' \
)
for (( i=0;i<12;i++ ))
do
#echo "${param[$i]} ${val[$i]}"
egrep "^[ #]*${param[$i]}.*" /etc/ssh/sshd_config &> /dev/null
if [ $? -eq 0 ];
then
sed -i "s|^[ #]*\$param[$i].*|${param[$i]} ${val[$i]}|1" /etc/ssh/sshd_config
else
echo "${param[$i]} ${val[$i]}" >> /etc/ssh/sshd_config
fi
done;
However the variable expansion in sed pattern match is not working as desired:
sed -i "s|^[ #]*\$param[$i].*|${param[$i]} ${val[$i]}|1" /etc/ssh/sshd_config
Can someone help me. My array expansion and everything in the script is fine though. I've checked the same with an echo printout.
Not sure why you have $ escaped, and to access array element you need to use ${param[$i]}.
You can use:
sed -i "s~^[ #]*${param[$i]}.*~${param[$i]} ${val[$i]}~1" /etc/ssh/sshd_config
btw ^[ #]* will only match space or # at line start.

Resources