How to append a character at the end of a specific line in a loop? - bash

I want to read line numbers from a file and according to that insert characters in another file. This is what I got so far:
#!/bin/bash
character=:
line_number=1
sed $line_number's/$/ '$character'/' <readme >readme_new
line_number=3
sed $line_number's/$/ '$character'/' <readme_new >readme_newer
I would like to do that in a loop now.

TL;DR:
$: c='!'
$: sed "s#\$# s/\$/ $c/#" fibs >script
$: sed -i "$(<script)" infile
Broken out -
A file of line numbers:
$: cat fibs
1
2
3
5
8
13
21
a file to be edited:
$: cat infile
1 a
2 b
3 c
4 d
5 e
6 f
7 g
8 h
9 i
10 j
11 k
12 l
13 m
14 n
15 o
16 p
17 q
18 r
19 s
20 t
21 u
22 v
23 q
24 x
25 y
26 z
3 steps -- first set your character variable if you're using one.
$: c='!'
Then make a script from the line number file -
$: sed "s#\$# s/\$/ $c/#" fibs >script
which creates:
$: cat script
1 s/$/ !/
2 s/$/ !/
3 s/$/ !/
5 s/$/ !/
8 s/$/ !/
13 s/$/ !/
21 s/$/ !/
It's a simple sed to add a sed substitution command for each line number, and sends the resulting script to a file. A few tricks here include using double-quotes to allow the character embedding, and #'s to allow the replacement text to include /'s without creating leaning-toothpick syndrome from all the backslash quoting.
Then run it against your input -
$: sed -i "$(<script)" infile
Which does the work. That pulls the script file contents in for sed to use, generating:
1 a !
2 b !
3 c !
4 d
5 e !
6 f
7 g
8 h !
9 i
10 j
11 k
12 l
13 m !
14 n
15 o
16 p
17 q
18 r
19 s
20 t
21 u !
22 v
23 q
24 x
25 y
26 z
Let me know if you want to tweak it.

Related

Divide an output into multiple variables using shell script

So I have a C program that outputs many numbers. I have to check them all. The problem is, each time I run my program, I need to change seeds. In order to do that, I've been doing it manually and was trying to make a shell script to work around this.
I've tried using sed but couldn't manage to do it.
I'm trying to get the output like this:
a=$(./algorithm < input.txt)
b=$(./algorithm2 < input.txt)
c=$(./algorithm3 < input.txt)
The output of each algorithm program is something like this:
12 13 315
1 2 3 4 5 6 7 8 10 2 8 9 1 0 0 2 3 4 5
So the variable a has all this output, and what I need is
variable a to contain this whole string
and variable a1 to contain only the third number, in this case, 315.
Another example:
2 3 712
1 23 15 12 31 23 3 2 5 6 6 1 2 3 5 51 2 3 21
echo $b should give this output:
2 3 712
1 23 15 12 31 23 3 2 5 6 6 1 2 3 5 51 2 3 21
and echo $b1 should give this output:
712
Thanks!
Not exactly what you are asking, but one way to do this would be to store the results of your algorithm in arrays, and then dereference the item of interest. You'd write something like:
a=( $(./algorithm < input.txt) )
b=( $(./algorithm2 < input.txt) )
c=( $(./algorithm3 < input.txt) )
Notice the extra () that encloses the statements. Now, a, b and c are arrays, and you can access the item of interest like ${a[0]} or $a[1].
For your particular case, since you want the 3rd element, that would have index = 2, hence:
a1=${a[2]}
b1=${b[2]}
c1=${c[2]}
Since you are using the Bash shell (see your tags), you can use Bash arrays to easily access the individual fields in your output strings. For example like so:
#!/bin/bash
# Your lines to gather the output:
# a=$(./algorithm < input.txt)
# b=$(./algorithm2 < input.txt)
# c=$(./algorithm3 < input.txt)
# Just to use your example output strings:
a="$(printf "12 13 315 \n 1 2 3 4 5 6 7 8 10 2 8 9 1 0 0 2 3 4 5")"
b="$(printf "2 3 712 \n 1 23 15 12 31 23 3 2 5 6 6 1 2 3 5 51 2 3 21")"
# Put the output in arrays.
a_array=($a)
b_array=($b)
# You can access the array elements individually.
# The array index starts from 0.
# (The names a1 and b1 for the third elements were your choice.)
a1="${a_array[2]}"
b1="${b_array[2]}"
# Print output strings.
# (The newlines in $a and $b are gobbled by echo, since they are not quoted.)
echo "Output a:" $a
echo "Output b:" $b
# Print third elements.
echo "3rd from a: $a1"
echo "3rd from b: $b1"
This script outputs
Output a: 12 13 315 1 2 3 4 5 6 7 8 10 2 8 9 1 0 0 2 3 4 5
Output b: 2 3 712 1 23 15 12 31 23 3 2 5 6 6 1 2 3 5 51 2 3 21
3rd from a: 315
3rd from b: 712
Explanation:
The trick here is that array constants (literals) in Bash have the form
(<space_separated_list_of_elements>)
for example
(1 2 3 4 a b c nearly_any_string 99)
Any variable that gets such an array assigned, automatically becomes an array variable. In the script above, this is what happens in a_array=($a): Bash expands the $a to the <space_separated_list_of_elements> and reads the whole expression again interpreting it as an array constant.
Individual elements in such arrays can be referenced like variables by using expressions of the form
<array_name>[<idx>]
like a variable name. Therein, <array_name>is the name of the array and <idx> is an integer that references the individual element. For arrays that are represented by array constants, the index counts elements continuously starting from zero. Therefore, in the script, ${a_array[2]} expands to the third element in the array a_array. If the array would have less elements, a_array[2] would be considered unset.
You can output all elements in the array a_array, the corresponding index array, and the number of elements in the array respectively by
echo "${a_array[#]}"
echo "${!a_array[#]}"
echo "${#a_array[#]}"
These commands can be used to track down the fate of the newline: Given the script above, it is still in $a, as can be seen by (watch the quotes)
echo "$a"
which yields
12 13 315
1 2 3 4 5 6 7 8 10 2 8 9 1 0 0 2 3 4 5
But the newline did not make it into the array a_array. This is because Bash considers it as part of the whitespace that separates the third and the fourth element in the array assignment. The same applies if there are no extra spaces around the newline, like here:
12 13 315\n1 2 3 4 5 6 7 8 10 2 8 9 1 0 0 2 3 4 5
I actually assume that the output of your C program comes in this form.
This will store the full string in a[0] and the individual fields in a[1-N]:
$ tmp=$(printf '12 13 315\n1 2 3 4 5 6 7 8 10 2 8 9 1 0 0 2 3 4 5\n')
$ a=( $(printf '_ %s\n' "$tmp") )
$ a[0]="$tmp"
$ echo "${a[0]}"
12 13 315
1 2 3 4 5 6 7 8 10 2 8 9 1 0 0 2 3 4 5
$ echo "${a[3]}"
315
Obviously replace $(printf '12 13 315\n1 2 3 4 5 6 7 8 10 2 8 9 1 0 0 2 3 4 5\n') with $(./algorithm < input.txt) in your real code.

Print variable inside awk while calculating variable name

I have a script that looks like the example below. I have a letter offset and I need to print the letter that I calculate with the offset. I am not sure how to read that letter using ksh.
My expected answer would be for LETTER_OFFSET(1)=a,LETTER_OFFSET(2)=v, LETTER_OFFSET(3)=c, etc. The offset I have it been calculated inside a loop.
#!/bin/ksh
# 1 2 3 4 5 6 7 8 9 10 11 12
LETTERS=" a v c d g r g s s a g f"
LETTER_OFFSET="3";
Letter=$(echo $LETTERS | awk '{print $((1 * $$LETTER_OFFSET )) }')
You'll pass your offset into your awk script to use as an awk variable using the awk -v flag:
LETTER=$(echo $LETTERS | awk -v offset=$LETTER_OFFSET '{print $offset}')
You don't need to invoke awk in every iteration. You can populate an array using your letters and then access it's values using index:
#!/bin/ksh
# 1 2 3 4 5 6 7 8 9 10 11 12
letters=" a v c d g r g s s a g f"
# populate an array
arr=($letters)
offset=1
while [ "$offset" -le 12 ]; do
echo "${arr[$offset-1]}"
let offset++
done
Output:
a
v
c
d
g
r
g
s
s
a
g
f

print a defined variable with preserving $

I would like to print 120 model list along with preserving the "$".
My script is:
#!/bin/sh
for i in {1..10};do
declare model="$"model$i
model_list=$(paste $model)
done
echo $model_list
After running it,
paste: $model1: No such file or directory
paste: $model2: No such file or directory
paste: $model3: No such file or directory
paste: $model4: No such file or directory
paste: $model5: No such file or directory
paste: $model6: No such file or directory
paste: $model7: No such file or directory
paste: $model8: No such file or directory
paste: $model9: No such file or directory
paste: $model10: No such file or directory
Desired output:
$model1 $model2 $model3 $model4 $model5 $model6 $model7 $model8 $model9 $model10
if you are interested in output instead of creating array the you can use below code
for i in {1..10};do
echo -n "\$model$i "
done
echo
or
for i in {1..10};do
echo -n '$model'$i' '
done
echo
or
for i in {1..10};do
model_list=$model_list" \$model$i"
done
echo $model_list
You have to escape the $ via \$ (backslash dollar-sign).
For instance:
#!/bin/sh
for i in {1..10}; do
declare model="\$"model$i
model_list+=$(paste $model)
done
echo $model_list
You can also shorten your script:
#!/bin/sh
for i in {1..10}; do
model_list+=$(paste "\$model${i}")
done
echo $model_list
Tested with 5 files titled $model1 - $model5:
File Contents:
$model1 - a b c d e f g
$model2 - 1 2 3 4 5 6 7 8 9 10
$model3 - I J K L M N O P
$model4 - 11 12 13 14 15 16 17 18 19 20
$model5 - q r s t u v w x y z
Output:
a b c d e f g1 2 3 4 5 6 7 8 9 10I J K L M N O P11 12 13 14 15 16 17 18 19 20q r s t u v w x y z

How to generate N columns with printf

I'm currently using:
printf "%14s %14s %14s %14s %14s %14s\n" $(cat NFE.txt)>prueba.txt
This reads a list in NFE.txt and generates 6 columns. I need to generate N columns where N is a variable.
Is there a simple way of saying something like:
printf "N*(%14s)\n" $(cat NFE.txt)>prueba.txt
Which generates the desire output?
# T1 is a white string with N blanks
T1=$(printf "%${N}s")
# Replace every blank in T with string %14s and assign to T2
T2="${T// /%14s }"
# Pay attention to that T2 contains a trailing blank.
# ${T2% } stands for T2 without a trailing blank
printf "${T2% }\n" $(cat NFE.txt)>prueba.txt
You can do this although i don't know how robust it will be
$(printf 'printf '; printf '%%14s%0.s' {1..6}; printf '\\n') $(<file)
^
This is your variable number of strings
It prints out the command with the correct number of string and executes it in a subshell.
Input
10 20 30 40 50 1 0
1 3 45 6 78 9 4 3
123 4
5 4 8 4 2 4
Output
10 20 30 40 50 1
0 1 3 45 6 78
9 4 3 123 4 5
4 8 4 2 4
You could write this in pure bash, but then you could just use an existing language. For example:
printf "$(python -c 'print("%14s "*6)')\n" $(<NFE.txt)
In pure bash, you could write, for example:
repeat() { (($1)) && printf "%s%s" "$2" "$(times $(($1-1)) "$2")"; }
and then use that in the printf:
printf "$(repeat 6 "%14s ")\n" $(<NFE.txt)

Unix Command (Mac OS): cut and move rows

Could you please give me a hint which unix command I can use to do the following:
I want to convert these lines...
1 a i
2 b ii
3 c iii
4 d iv
5 e v
6 f vi
7 g vii
8 h viii
9 i xi
...into those:
1 a i 4 d iv 7 g vii
2 b ii 5 e v 8 h viii
3 c iii 6 f vi 9 i xi
rsand perl -pne just transpose them but I need a completely new arrangement as you see. Perl-code would be favored, but I am thankful for any help.
cheers
marsch
Using a perl one-liner
perl -lne 'push #{$l[($.-1) % 3]}, $_; }{ print "#$_" for #l' data.txt | column -t
Explanation:
Switches:
-l: Enable line ending processing, specifies line terminator
-n: Creates a while(<>){..} loop for each line in your input file.
-e: Tells perl to execute the code on command line.
Code:
push #{$l[($.-1) % 3]}, $_;: Push each line into an array modulo the line number
}{ print "#$_" for #l: Print the 3 element array at end of processing
| column -t: Even out the columns
I would go with split and paste from coreutils. Try the following commands:
split -l3 infile
paste -d' ' xaa xab xac | column -t
Output:
1 a i 4 d iv 7 g vii
2 b ii 5 e v 8 h viii
3 c iii 6 f vi 9 i xi
Here is a oneliner:
perl -ne 'chomp; push #a,$_ if $_; unless($. % 3) {push #f,[#a]; #a = undef; shift #a} END {for my $i (#f) { for (#$i) {print "$_ "} print "\n"}}' filename.txt
output
1 a i 2 b ii 3 c iii
4 d iv 5 e v 6 f vi
7 g vii 8 h viii 9 i xi
I use ruby
string = "1 a i
2 b ii
3 c iii
4 d iv
5 e v
6 f vi
7 g vii
8 h viii
9 i xi "
ary = string.split("\n")
length = ary.size / 3
new_ary = Array.new(3, "")
ary.each_with_index do |e, i|
position = i % 3
new_ary[position] += e
end
puts new_ary.join("\n")
Hope to help:)

Resources