bash: string concatenation with newline - bash

I try to append strings to a string in a bash script.
my code:
page_list=""
for ... do
$page_list+=$"duration $mdelay \n"
done
echo $page_list >> $list_file
But it gives the following error
+=duration 0.12 \n: command not found
Update: removing the leading $ before page_list resolves the problem, but no new line is added to the list_file.
Update 2: None of the solutions offered works

By using $ in at the beginning of the string, Bash is trying to run it as a command, just remove it
page_list=""
page_list+="duration $mdelay \n"

You can use the $'\n' bashism, or you can simply do:
page_list="duration $mdelay
"
or
nl='
'
page_list="duration ${mdelay}${nl}"

I wish I could comment to ask for more clarification on what $list_file is. The key here should be to echo within the loop and output the result to a text file. The result of each echo will be written onto a newline automatically.
#!/bin/bash
file_list="one two three"
mdelay="0.12"
for i in $file_list
do
i+=" duration $mdelay"
echo $i >> output.txt
done
Result (in output.txt):
one duration 0.12
two duration 0.12
three duration 0.12

Related

how to assign each of multiple lines in a file as different variable?

this is probably a very simple question. I looked at other answers but couldn't come up with a solution. I have a 365 line date file. file as below,
01-01-2000
02-01-2000
I need to read this file line by line and assign each day to a separate variable. like this,
d001=01-01-2000
d002=02-01-2000
I tried while read commands but couldn't get them to work.It takes a lot of time to shoot one by one. How can I do it quickly?
Trying to create named variable out of an associative array, is time waste and not supported de-facto. Better use this, using an associative array:
#!/bin/bash
declare -A array
while read -r line; do
printf -v key 'd%03d' $((++c))
array[$key]=$line
done < file
Output
for i in "${!array[#]}"; do echo "key=$i value=${array[$i]}"; done
key=d001 value=01-01-2000
key=d002 value=02-01-2000
Assumptions:
an array is acceptable
array index should start with 1
Sample input:
$ cat sample.dat
01-01-2000
02-01-2000
03-01-2000
04-01-2000
05-01-2000
One bash/mapfile option:
unset d # make sure variable is not currently in use
mapfile -t -O1 d < sample.dat # load each line from file into separate array location
This generates:
$ typeset -p d
declare -a d=([1]="01-01-2000" [2]="02-01-2000" [3]="03-01-2000" [4]="04-01-2000" [5]="05-01-2000")
$ for i in "${!d[#]}"; do echo "d[$i] = ${d[i]}"; done
d[1] = 01-01-2000
d[2] = 02-01-2000
d[3] = 03-01-2000
d[4] = 04-01-2000
d[5] = 05-01-2000
In OP's code, references to $d001 now become ${d[1]}.
A quick one-liner would be:
eval $(awk 'BEGIN{cnt=0}{printf "d%3.3d=\"%s\"\n",cnt,$0; cnt++}' your_file)
eval makes the shell variables known inside your script or shell. Use echo $d000 to show the first one of the newly defined variables. There should be no shell special characters (like * and $) inside your_file. Remove eval $() to see the result of the awk command. The \" quoted %s is to allow spaces in the variable values. If you don't have any spaces in your_file you can remove the \" before and after %s.

How to prepend to a string that comes out of a pipe

I have two strings saved in a bash variable delimited by :. I want to get extract the second string, prepend that with THIS_VAR= and append it to a file named saved.txt
For example if myVar="abc:pqr", THIS_VAR=pqr should be appended to saved.txt.
This is what I have so far,
myVar="abc:pqr"
echo $myVar | cut -d ':' -f 2 >> saved.txt
How do I prepend THIS_VAR=?
printf 'THIS_VAR=%q\n' "${myVar#*:}"
See Shell Parameter Expansion and run help printf.
The more general solution in addition to #konsolebox's answer is piping into a compound statement, where you can perform arbitrary operations:
echo This is in the middle | {
echo This is first
cat
echo This is last
}

unix shell replace string twice (in one line)

I run a script with the param -A AA/BB . To get an array with AA and BB, i can do this.
INPUT_PARAM=(${AIRLINE_OPTION//-A / }) #get rid of the '-A ' in the begining
LIST=(${AIRLINES_PARAM//\// }) # split by '/'
Can we achieve this in a single line?
Thanks in advance.
One way
IFS=/ read -r -a LIST <<< "${AIRLINE_OPTION//-A /}"
This places the output from the parameter substitution ${AIRLINE_OPTION//-A /} into a "here-string" and uses the bash read built-in to parse this into an array. Splitting by / is achieved by setting the value of IFS to / for the read command.
LIST=( $(IFS=/; for x in ${AIRLINE_OPTION#-A }; do printf "$x "; done) )
This is a portable solution, but if your read supports -a and you don't mind portability then you should go for #1_CR's solution.
With awk, for example, you can create an array and store it in LIST variable:
$ LIST=($(awk -F"[\/ ]" '{print $2,$3}' <<< "-A AA/BB"))
Result:
$ echo ${LIST[0]}
AA
$ echo ${LIST[1]}
BB
Explanation
-F"[\/ ]" defines two possible field separators: a space or a slash /.
'{print $2$3}' prints the 2nd and 3rd fields based on those separators.

command working in command line but not thru script

cat test.txt
#this is comment
line 1
line 2
#this is comment at line 3
line4
script:
result=`awk '/^#.*/ { print }' test.txt `
for x in $result
do
echo x
done
expected output:
#this is comment
#this is comment at line 3
getting output:
#this
is
comment
#this
is
comment
at
line
3
but when i execute this command awk '/^#.*/ { print }' test.txt,
i get expected result.
I am putting this in loop because I need to capture each comment one at a time, not all together.
This happens because for x in $result will loop through each word in $result - that's what for is meant to do.
Try this instead:
echo "$result" | while read x; do
echo "$x"
done
read will take one line at a time, which is what you need here.
Your issue is not the awk part, but is the for part. When you do
for x in yes no maybe why not
do
echo x
done
You'll get
yes
no
maybe
why
not
That is, the list that for is looping over is automatically space-delimited.
One fix, I suppose, is to wrap the comments in quotes; then for will treat each quoted comment as a single item. legoscia's fix (using read in a while loop) seems even better to me.

Using output of command to generate autocompletion commands for zsh

Hey, I'm trying to get zsh to run a git command, and use the output to generate autocomplete possibilities.
The command I'm trying to run is
git log -n 2 --pretty=format:"'%h %an'"
And here's the code I'm using:
local lines words
lines=(${(f)$(git log -n 2 --pretty=format:"'%h %an'")})
words=${(f)$(_call_program foobar git log -n 2 --pretty=format:"%h")}
echo "Length of lines is " ${#lines[#]} " value is " ${lines}
echo "Length of words is " ${#words[#]} " value is " ${words}
compadd -d lines -a -- words
This doesn't work at all...it thinks that words is a single element and lines aren't getting printed properly at all.
However, when I try to setup an array of strings by hand, it all works.
local lines words
lines=('one two' 'three')
words=('one two' 'three')
echo "Length of lines is " ${#lines[#]} " value is " ${lines}
echo "Length of words is " ${#words[#]} " value is " ${words}
compadd -d lines -a -- words
To force words being an array, you should use either
words=( ${(f)...} )
or
set -A words ${(f)...}
. If you use just words=${(f)...}, you will always get one value. By the way, why have you added parenthesis around ${(f)...} when you were writing lines definition, but have not done it for words?
Also, there is another thing to concern: ${(f)$(...)} should be replaced with ${(f)"$(...)"}. It is some black magic here: I don't know why first one does emit a single scalar value, while second one does emit an array of scalar values, just was pointed to this fact by someone here on stackoverflow.
Thanks for the help, ZyX, here's the final script for anyone who cares
local lines words
lines=(${(f)"$(git log -n 15 --pretty=format:"'%h - %an - %s'")"} )
words=(${(f)"$(git log -n 15 --pretty=format:"%h")"})
compadd -l -d lines -a -- words
I had a more complicated situation. I was trying to grep many files for a string, then edit the resulting list of files. The use of ** and * wildcards didn't let the above solution work for me. I did get it to work by breaking up into 2 steps:
> tmp=$(grep -l someString **/*.clj)
> fileList=( ${(f)tmp} )

Resources