I want to output a specific line from bash argument using sed, I have tried many ways, but none work:
#!/bin/bash
sed -n '$2p' $1
sed -n '${2}p' $1
sed -n "$2p" $1
sed -n "${2}p" $1
sed -n ''"$2"'p' $1
How on earth do I get the correct result?
Try
sed -n "$2p" $1
Demo:
$seq 10 > file.txt
$cat temp.ksh
#!/bin/bash
set -x
sed -n "$2p" $1
$./temp.ksh file.txt 3
+ sed -n 3p file.txt
3
$
Related
For the following MVCE:
echo "test_num: 0" > test.txt
test_num=$(grep 'test_num:' test.txt | cut -d ':' -f 2)
new_test_num=$((test_num + 1))
echo $test_num
echo $new_test_num
sed -i "s/test_num: $test_num/test_num: $new_test_num/g" test.txt
cat test.txt
echo "sed -i "s/test_num: $test_num/test_num: $new_test_num/g" test.txt"
sed -i "s/test_num: 0/test_num: 1/g" test.txt
cat test.txt
Which outputs
0 # parsed original number correctly
1 # increment the number
test_num: 0 # sed with interpolated variable, does not work
sed -i s/test_num: 0/test_num: 1/g test.txt # interpolated parameter looks right
test_num: 1 # ???
Why does sed -i "s/test_num: $test_num/test_num: $new_test_num/g" test.txt not produce the expected result when sed -i "s/test_num: 0/test_num: 1/g" test.txt works just fine in the above example?
As mentioned in the comment, there is a white space in ${test_num}. Therefore in your sed there should not be an empty space between the colon and your variable.
Also I guess you should surround your variable with curly bracket {} to increase readability.
sed "s/test_num:${test_num}/test_num: ${new_test_num}/g" test.txt
test_num: 1
If you just want the number in ${test_num}, you can try something like:
grep 'test_num:' test.txt | awk -F ': ' '{print $2}'
awk allows to specify delimiter with more than 1 character.
Instead of grep|cut you can also use sed.
#! /bin/bash
exec <<EOF
test_num: 0
EOF
grep 'test_num:' | cut -d ':' -f 2
exec <<EOF
test_num: 0
EOF
sed -n 's/^test_num: //p'
When using regexp replace in sed there is special meaning to $ .
Suggesting to rebuild your sed command segments as follow:
sed -i 's/test_num: '$test_num'/test_num: '$new_test_num'/g' test.txt
Other option, use echo command to expand variables in sed command.
sed_command=$(echo "s/test_num:${test_num}/test_num: ${new_test_num}/g")
sed -i "$sed_command" test.txt
I wrote the following command:
var=$(printf "$line" | awk -F':' {'print $2'} | sed 's/,//g')
sed -i "s/$var/exampletext/g" "file.txt"
The text is part of a larger script that finds a specific value, and then replaces it with another value.
The basic end result I want is something like this:
sed -i "s/123/exampletext/g" "file.txt"
That should replace all of the 123's in file.txt with exampletext. When I echo $var, it shows the result of 123. Yet when that command is run, it doesn't work - it does not replace 123 with exampletext.
Here is the full script:
INPUT="$1"
while IFS= read -r line;
do
if [[ $line == *"length"* ]];
then
var=$(printf "$line" | awk -F':' {'print $2'} | sed 's/,//g')
sed -i "s|$var|cookiecrisp|g" "vomit.txt"
fi
done < $INPUT
Sample data in a stackoverflow.csv file I have:
foo
foo
foo
foo
bar
bar
bar
bar
baz
baz
baz
baz
I know with sed -n /foo/p stackoverflow.csv > foo.csv
I'll get all records matching foo directed to that file, but I don't want to specify the matching pattern on the cli, I'd rather put it in a script and have all records (foo, bar and baz) sent to their own file.
Basically this in a script:
sed -n /foo/p stackoverflow.csv > foo.csv
sed -n /bar/p stackoverflow.csv > bar.csv
sed -n /baz/p stackoverflow.csv > baz.csv
Like this:
#!/bin/sh
sleep 2
sed -n /foo/p > foo.csv
sed -n /bar/p > bar.csv
sed -n /baz/p > baz.csv
This creates the files but they're all empty.
Also, if the script were to have just one print statement, it works.
Any input?
You missed the input filename
#!/bin/sh
sleep 2
sed -n /foo/p stackoverflow.csv > foo.csv
sed -n /bar/p stackoverflow.csv > bar.csv
sed -n /baz/p stackoverflow.csv > baz.csv
You can provide input file as an argument to your script, in that case change the script to read an argument and pass it to the sed command.
Run the script like this: ./script.sh input_filename, where you can specify different input files as argument.
#!/bin/sh
file=${1}
sed -n /foo/p ${file} > foo.csv
sed -n /bar/p ${file} > bar.csv
sed -n /baz/p ${file} > baz.csv
Here's a solution with grep in a loop where you don't need to provideeach term in advance. Assuming a bash shell:
for i in $(sort stackoverflow.csv | uniq); do grep $i testfile > $i; done
The w command in sed allows you to write to a named file.
sed -n -e '/foo/wfoo.csv' -e '/bar/wbar.csv' \
-e '/baz/wbaz.csv' stackoverflow.csv
Not all sed dialects accept multiple -e arguments; a multi-line string with the commands separated by newlines may be the most portable solution.
sed -n '/foo/wfoo.csv
/bar/wbar.csv
/baz/wbaz.csv' stackoverflow.csv
This examines the input file just once, and writes out the matching lines as it proceeds through the file.
To create a script which accepts one or more file names as arguments, replace the hard-coded file name with "$#".
#!/bin/sh
sed -n ... "$#"
I want to print the text at a specified line number from a file.
Here is my bash script
line=12
sed -n "$line{p;q;}"
My line number comes in a variable. But the above code is not working. What should I do?
Using sed
line=12
sed -n "${line}p" my_file
# Multiple lines
line1=10
line2=15
sed -n "${line1},${line2}p" my_file
In awk:
awk "NR==${line}" my_file
# Multiple lines
awk "NR >= ${line1} && NR <= ${line2}" my_file
Or using head and tail but probably not as efficient:
head -${line} my_file | tail -1
# Multiple lines
head -${line2} my_file | tail -$(($line2-$line1+1))
You have to give the file name as an argument to sed.
line=12
sed -n "$line{p;q;}" filename
If you are passing the filename as an argument to a bash script, you need to use:
line=12
sed -n "$line{p;q;}" "$1"
Fast sed command (useful for bigger files) is:
n=12; sed $n'q;d' file
>cat /tmp/list1
john
jack
>cat /tmp/list2
smith
taylor
It is guaranteed that list1 and list2 will have equal number of lines.
f(){
i=1
while read line
do
var1 = `sed -n '$ip' /tmp/list1`
var2 = `sed -n '$ip' /tmp/list2`
echo $i,$var1,$var2
i=`expr $i+1`
echo $i,$var1,$var2
done < $INFILE
}
So output of f() should be:
1,john,smith
2,jack,taylor
But getting
1,p,p
1+1,p,p
If i replace following:
var1 = `sed -n '$ip' /tmp/list1`
var2 = `sed -n '$ip' /tmp/list2`
with this:
var1=`head -$i /tmp/vip_list|tail -1`
var2=`head -$i /tmp/lb_list|tail -1`
Then output:
1,john,smith
1,john,smith
If you can use paste and awk command, you can achieve the same with a one-liner:
paste -d, /tmp/list1 /tmp/list2 | awk '{print NR "," $0}'
Replace the while script with this line :)
the $ip is the problem there making ip the name of the variable, you should use ${i}p instead letting the shell know that the variable is i not ip, your code should look like
var1=`sed -n "${i}p" /tmp/list1`
var2=`sed -n "${i}p" /tmp/list2`