I have a bash script which runs as follows:
./script.sh var1 var2 var3.... varN varN+1
What i need to do is take first 2 variables, last 2 variables and insert them into file. The variables between 2 last and 2 first should be passed as a whole string to another file. How can this be done in bash?
Of course i can define a special variable with "read var" directive and then input this whole string from keyboard but my objective is to pass them from the script input
argc=$#
argv=("$#")
first_two="${argv[#]:0:2}"
last_two="${argv[#]:$argc-2:2}"
others="${argv[#]:2:$argc-4}"
#!/bin/bash
# first two
echo "${#:1:2}"
# last two
echo "${#:(-2):2}"
# middle
echo "${#:3:(($# - 4))}"
so sample
./script aaa bbb ccc ddd eee fff gggg hhhh
aaa bbb
gggg hhhh
ccc ddd eee fff
Related
I am trying to use bash to merge/combine all text files in a directory with the same prefix into one text file. Thank you :).
directory
111.txt
aaa
aaa
222_1.txt
bbb
222_2.txt
ccc
ccc
333_1.txt
aaa
333_2.txt
ccc
ccc
333_3.txt
bbb
desired
111.txt
aaa
aaa
222.txt
bbb
ccc
ccc
333.txt
aaa
ccc
ccc
bbb
bash
for file in `ls`|cut -d"_" -f1 ; do
cat ${file}_* > ${file}
done
This is a good use of an associative array as a set. Iterate over the file names, trimming the trailing _* from each name before adding it to the associative array. Then you can iterate over the array's keys, treating each one as a filename prefix.
# IMPORTANT: Assumes there are no suffix-less file names that contain a _
declare -A prefixes
for f in *; do
prefixes[${f%_*}]=
done
for f in "${!prefixes[#]}"; do
[ -f "$f".txt ] && continue # 111.txt doesn't need anything done
cat "$f"_* > "$f".txt
done
build a test environment just as you did
mkdir -p tmp/test
cd !$
touch {111,222,333}.{txt,_2.txt,_3.txt}
cat > 111.txt
aaa
aaa
and so on
then you know how to increment filnames :
for i in $( seq 1 3 ) ; do echo $i* ; done
111._2.txt 111._3.txt 111.txt
222._2.txt 222._3.txt 222.txt
333._2.txt 333._3.txt 333.txt
so you make your resulting files and here is the answer of mechanism to your needs :
for i in $( seq 1 9 ) ; do cat $i* >> new.$i.txt ; done
and finaly
ls -l new.[1-3]*
-rw-r--r-- 1 francois francois 34 Aug 4 14:04 new.1.txt
-rw-r--r-- 1 francois francois 34 Aug 4 14:04 new.2.txt
-rw-r--r-- 1 francois francois 34 Aug 4 14:04 new.3.txt
all 3* contents are in new.".txt for example here.
you only have to set the desired file destination to add in the content & if needed but not set in initial question a sorting of datas by alphabetic order or numerical... etc
I have two variables in bash:
in the first variable, the field separator is (,)
in the second variable, the field separator is also (,)
in the first variable named VAR1 I have:
Maria Debbie Annie,Chewbakka Zero,Yoda One,Vader 001
in the second variable named VAR2:
"number":"11112",Maria Debbie Annie
"number":"11113",Maria Debbie Annie Lisa
"number":"33464",Chewbakka Zero
"number":"22465",Chewbakka Zero Two
"number":"34534",Christine Ashley
"number":"45233",Yoda One
"number":"45233",Yoda One One
"number":"38472",Susanne Ann
"number":"99999",Vader 001
"number":"99991",Vader 001 001
"number":"99992",Vader 001 002
The desired output in variable VAR3:
"number":"11112","number":"33464","number":"45233","number":"99999"
So basically i need to change the names in the output from some name to "number":"somenumber" the same order as in the first variable.
What is also important that there are very similar strings so
Yoda One != Yoda One One also Chewbakka Zero is not equal Chewbakka Zero Two.
VAR2 contains much more lines than listed, I just wanted to show the script needs to find exact matches between VAR1 and VAR2.
Thank you for the help.
Check this out..
> echo "$VAR1"
Maria Debbie Annie,Chewbakka Zero,Yoda One,Vader 001
> echo "$VAR2"
"number":"11112",Maria Debbie Annie
"number":"11113",Maria Debbie Annie Lisa
"number":"33464",Chewbakka Zero
"number":"22465",Chewbakka Zero Two
"number":"34534",Christine Ashley
"number":"45233",Yoda One
"number":"45233",Yoda One One
"number":"38472",Susanne Ann
"number":"99999",Vader 001
"number":"99991",Vader 001 001
"number":"99992",Vader 001 002
> export VAR1A=$(echo $VAR1| sed 's/,/$\|/g' | sed 's/$/\$/g')
> echo "$VAR1A"
Maria Debbie Annie$|Chewbakka Zero$|Yoda One$|Vader 001$
> echo "$VAR2" | egrep "$VAR1A" | awk -F"," ' { printf("%s,",$1)} END { printf("\n") } ' | sed 's/.$//g'
"number":"11112","number":"33464","number":"45233","number":"99999"
>
I want to catch the row contain "Will_Liu>" from massive_data.txt if the n < m or n==0 or m==0, a period of the prototype is as below.
cat massive_data.txt
Will_Liu> set Name.* xxx
============================================
Id Name Para status
============================================
1 name-1 xxxxx OK
2 name-2 xxxxx OK
3 name-3 xxxxx Not_OK
. ... .... OK
. ... .... OK
m name-m .... Not_OK
============================================
Total: m name attempted, n name set OK
In the above code, the "m" and "n" are variable, if the n < m or n==0 or m==0, print the rows contain "Will_Liu>" ;
if n==m and both of them !=0, just skip and ignore this situation.
I just could use "grep" and "sed" to grasp key points like those:
cat test.txt
Will_Liu> set Name_group1 xxx
============================================
Id Name Para status
============================================
1 name-1 xxxxx OK
2 name-2 xxxxx OK
3 name-3 xxxxx Not_OK
============================================
Total: 3 name attempted, 2 name set OK
Will_Liu> set Name_group2 yyy
============================================
Id Name Para status
============================================
1 name-4 xxxxx OK
2 name-5 xxxxx Not_OK
3 name-6 xxxxx Not_OK
============================================
Total: 3 name attempted, 1 name set OK
I could use "sed" and "grep" command like this:
sed -n "/Total: 3 name attempted,/p" test.txt
Total: 3 name attempted, 2 name set OK
Total: 3 name attempted, 1 name set OK
grep -B 9 "Total: 3 name attempted" test.txt | sed -n '/Will_Liu>/p'
Will_Liu> set Name_group1 xxx
Will_Liu> set Name_group2 yyy
in the grep command the 9 is 3+6, the 6 is base on the format of the structure, it's a fixed value.
So how can I introduce 2 variates to define the "m" and "n" and improve my code to get expected result from massive_data.txt?
My expect output:
Will_Liu> set Name1 xxx
Will_Liu> set Name2 yyy
Will_Liu> set Name3 zzz
. . .
. . .
. . .
In general, any previous line you want to print matches another pattern. In these cases it is better to store the last candidate to be printed and when you reach your condition, decide what to do with it. For example
awk '/^Will_Liu/{
last_will=$0
}
/^Total/{
m=$2; n=$5
if (m>n || (m==0 && n==0))
print last_will
}' file
In cases where you really don't have any pattern to select the last candidate to print, and you have to decide some line number to print after a math operation on matched line data, then you could double pass a file, or use tac to invert the input or keep all last lines in a hash array or any similar approach. These approaches could be not efficient sometimes. For example, with storing all lines, which is not recommended for your case
awk '{ line[NR]=$0 }
/^Total/{
m=$2; n=$5
if (m>n || (m==0 && n==0))
print line[NR-(m+5)]
}' file
How can I take sort bash arguments alphabetically?
$ ./script.sh bbb aaa ddd ccc
and put it into an array such that I now have an array {aaa, bbb, ccc, ddd}
You can do:
A=( $(sort <(printf "%s\n" "$#")) )
printf "%s\n" "${A[#]}"
aaa
bbb
ccc
ddd
It is using steps:
sort the arguments list i.e."$#"`
store output of sort in an array
Print the sorted array
I hope following 2 lines will help.
sorted=$(printf '%s\n' "$#"|sort)
echo $sorted
This will give you a sorted cmdline args.I wonder though why its needed :)
But anyway it will sort your cmdlines
Removed whatever was not required.
Here's an invocation that breaks all the other solutions proposed here:
./script.sh "foo bar" "*" "" $'baz\ncow'
Here's a piece of code that works correctly:
array=()
(( $# )) && while IFS= read -r -d '' var
do
array+=("$var")
done < <(printf "%s\0" "$#" | sort -z)
As there seem not appreciate my effort to reducing forks, there is a better solution than using IFS for parsing and setting a variable
Part 1: Very short and robust solution:
As suggested by #rici in a comment on another post, I add the -t argument to mapfile:
mapfile -t args < <(sort < <(printf "%s\n" "$#"))
This work with white space too.
Sample:
#!/bin/bash
mapfile args < <(sort < <(printf "%s\n" "$#"))
mapfile -t args < <(sort < <(printf "%s\n" "$#"))
declare -p args
for (( i=0 ; i<${#args[#]} ;i++));do
printf "%3d: %s\n" $i "${args[i]%$'\n'}"
printf "%3d: %s\n" $i "${args[i]}"
done
run sample:
/tmp/script ccc "a a" aaa ddd aa AA z aab
declare -a args='([0]="aa
" [1]="a a
" [2]="AA
" [3]="aaa
" [4]="aab
" [5]="ccc
" [6]="ddd
" [7]="z
")'
0: aa
1: a a
2: AA
3: aaa
4: aab
5: ccc
6: ddd
7: z
Part 2: Very quick: pure bash way (without forks!)
Nota: Of course, this is not the better, robust way of doing sort, but in many cases, this could efficiently be used.
As (at least) one guy seem prefer a to be sorted before aa, this is edited to replace z by 0.
This sample is limited to 1st 6 chars but you could replace 6 by bigger number but add same number of z.
#!/bin/bash
sep='§'
for i;do
a=${i//[^a-zA-Z0-9]/0}000000
args[36#${a:0:6}]+=${args[36#${a:0:6}]+$sep}${i}
done
IFS=$sep args=(${args[*]})
printf "%s\n" ${args[#]}
declare -p args
For case sensitivity, you could replace 36# by 64#:
Working sample:
#!/bin/bash
sep=§
base=64
chars=8
fillc=0
usage() {
cat <<eousage
Usage: $0 [-ai] [-p precision] [-s inner separator]
-a for sorting \`\`empty'' After (\`\`aa'' after \`\`aaa'')
-i for case Insensitive
-p NUM tell the number of characters to compare (default: $chars)
-s SEP let you precise inner separator, (default \`\`$sep'')
eousage
}
while getopts "iap:s:" opt;do case $opt in
a ) fillc=z ;;
i ) base=36 ;;
p ) chars=$OPTARG ;;
s ) sep=$OPTARG ;;
* ) usage ; exit 1 ;;
esac ; done ;
shift $[OPTIND-1]
printf -v cfill "%${chars}s"
cfill=${cfill// /$fillc}
for i;do
a=${i//[^a-zA-Z0-9]/$fillc}$cfill
idx=$[$base#${a:0:$chars}]
args[$idx]+=${args[$idx]+$sep}${i}
done
declare -p args
IFS=$sep args=(${args[*]})
declare -p args
for (( i=0 ; i++<${#args[#]} ;b));do
printf "%3d: %s\n" $i ${args[i-1]}
done
Run cases:
/tmp/script ccc aaa ddd aa AA z aab
declare -a args='([44667659878400]="aa" [44678397296640]="aaa"
[44679471038464]="aab" [53614076755968]="ccc" [58081916485632]="ddd"
[153931627888640]="z" [160803575562240]="AA")'
declare -a args='([0]="aa" [1]="aaa" [2]="aab" [3]="ccc" [4]="ddd"
[5]="z" [6]="AA")'
1: aa
2: aaa
3: aab
4: ccc
5: ddd
6: z
7: AA
Case insensitive:
/tmp/script -i ccc aaa ddd aa AA z aab
declare -a args='([805409464320]="aa§AA" [806014126080]="aaa"
[806074592256]="aab" [967216951296]="ccc" [1047818363904]="ddd"
[2742745743360]="z")'
declare -a args='([0]="aa" [1]="AA" [2]="aaa" [3]="aab" [4]="ccc"
[5]="ddd" [6]="z")'
1: aa
2: AA
3: aaa
4: aab
5: ccc
6: ddd
Empty sorted after:
/tmp/script -ia ccc aaa ddd aa AA z aab
declare -a args='([806074592255]="aaa" [806135058431]="aab"
[807586246655]="aa§AA" [967277417471]="ccc" [1047878830079]="ddd"
[2821109907455]="z")'
declare -a args='([0]="aaa" [1]="aab" [2]="aa" [3]="AA" [4]="ccc"
[5]="ddd" [6]="z")'
1: aaa
2: aab
3: aa
4: AA
5: ccc
6: ddd
7: z
precision: 1 chars:
/tmp/script -iap1 ccc aaa ddd aa AA z aab
declare -a args='([10]="aaa§aa§AA§aab" [12]="ccc" [13]="ddd" [35]="z")'
declare -a args='([0]="aaa" [1]="aa" [2]="AA" [3]="aab" [4]="ccc"
[5]="ddd" [6]="z")'
1: aaa
2: aa
3: AA
4: aab
5: ccc
6: ddd
7: z
and precision: 10 chars:
/tmp/script -p 10 ccc aaa ddd aa AA z aab
declare -a args='([182958734861926400]="aa" [183002715327037440]="aaa"
[183007113373548544]="aab" [219603258392444928]="ccc"
[237903529925148672]="ddd" [630503947831869440]="z"
[658651445502935040]="AA")'
declare -a args='([0]="aa" [1]="aaa" [2]="aab" [3]="ccc" [4]="ddd"
[5]="z" [6]="AA")'
1: aa
2: aaa
3: aab
4: ccc
5: ddd
6: z
7: AA
Whitespaces and other chars:
/tmp/script -is # ccc "a a" aaa ddd 'a*a' 'a§a' aa AA z aab
declare -a args='([784246302720]="a a#a*a#a§a" [805409464320]="aa#AA"
[806014126080]="aaa" [806074592256]="aab" [967216951296]="ccc"
[1047818363904]="ddd" [2742745743360]="z")'
declare -a args='([0]="a a" [1]="a*a" [2]="a§a" [3]="aa" [4]="AA"
[5]="aaa" [6]="aab" [7]="ccc" [8]="ddd" [9]="z")'
1: a a
2: a*a
3: a§a
4: aa
5: AA
6: aaa
7: aab
8: ccc
9: ddd
10: z
This question already has an answer here:
Closed 11 years ago.
Possible Duplicate:
Shell script to remove new line after numeric string
I need to write a SHELL Script to remove new line \n after string doesn't start with number and substitute it to tab \t or for 5 spaces for example.
For example a have a file:
asasas
12345
adab-123
123
I need output like this:
asasasi 12345
adab-123 123
This should work -
[jaypal~/Temp]$ cat file9
asasas
12345
adab-123
123
fffd
223
2323
afdf
23234
with tab:
[jaypal~/Temp]$ sed '/^[0-9]/!{N;s/\n/\t/}' file9
asasas 12345
adab-123 123
fffd 223
2323
afdf 23234
The awk way =)
awk '/^[0-9]/{print} !/^[0-9]/{printf("%s\t", $0)}' file
same output as Jaypal.