I have a file that is something like below.
I want to grep for a string say cde and find two lines above it and delete in the same file (something like perl -i).
abc
abc
cde
fgh
lij
lij
klm
mno
pqr
pqr
I tried
grep -B 2 "cde" a.txt
Output
abc
abc
cde
But now I want to delete the two lines above cde so that my final output is
cde
fgh
lij
lij
klm
mno
pqr
pqr
I have tried
grep -v -B "cde" a.txt
but it doesn't work
In a perl one-liner
perl -ne 'push #b, $_; #b = () if /^cde$/; print shift #b if #b == 3; END { print #b }' file.txt
Here is an awk solution.
awk 'FNR==NR {if (/cde/) f=NR;next} FNR!=f-1 && FNR!=f-2' file{,} > tmp && mv tmp file
cde
fgh
lij
lij
klm
mno
pqr
pqr
file{,} is the same as file file. Make awk read the file two times.
First time look for cde and store it in a variable f
Second time print if record is not -1 or -2 compare to the finding
> tmp && mv tmp file store output in a tmp file, then write back to original file, like -i
Related
I have a file1 which has lines
abc
dfg
hij
cab
I would like to match the pattern ab in file1 and print to file2. After that delete those lines from file1. Then file1 has the following lines
dfg
hij
and file2 has lines
abc
cab
Currently, I am doing it with two lines of code
perl -ne '/ab/i && print' file1.csv > file2.csv
perl -n -i.bak -e 'print unless m/ab/' file1.csv
Can any one give me a one-liner Perl code?
Thanks,
I'd use ed instead:
$ rm file2.csv # Just in case it already exists
$ ed -s file1.csv <<'EOF'
g/ab/.W file2.csv\
d
w
EOF
(For every line matching the basic regular expression ab, append it to file2.csv and delete the line, and then finally write the modified buffer back to file1.csv).
But one simple perl one-liner:
$ perl -ni -e 'if (/ab/) { print STDERR } else { print }' file1.csv 2>file2.csv
This prints matching lines to standard error and uses the shell to redirect that to file2.csv, while modifying file1.csv in-place with just the non-matching lines.
perl -i -ne'print { /ab/ ? *STDERR : select() } $_' file1.csv 2>file2.csv
But error messages end up in file2.csv.
These versions don't suffer from that problem:
perl -i -ne'
BEGIN { open $fh, ">&=", 3; }
print { /ab/ ? $fh : select() } $_;
' file1.csv 3>file2.csv
perl -i -ne'
BEGIN { open $fh, ">", shift(#ARGV); }
print { /ab/ ? $fh : select() } $_;
' file2.csv file1.csv
I want to reverse the sign of numbers in column x (2) in multiple files. For example:
From
1 | 2.0
2 | -3.0
3 | 1.0
To
1 |-2.0
2 |3.0
3 |-1.0
I am using sed '/^-/ {s/.//;b};s/^/-/' file command, but it does not work. Any suggestion?
A more "proper" way using actual math is easy with awk. For example if you want to negate columns 2 and 3:
awk '{print $1, -$2, -$3}'
$ cat ip.txt
1 | 2.0
2 | -3.0
3 | 1.0
Modifying sed command from OP, not suited to easily modify for a different column or different delimiter
$ sed -E '/^(.*\|\s*)-[0-9]/ {s/^(.*\|\s*)-/\1/;b}; s/^(.*\|\s*)/&-/' ip.txt
1 | -2.0
2 | 3.0
3 | -1.0
With perl where it is easier to specify delimiter and modify specific column
$ perl -F'\|' -lane '$F[1] =~ m/-/ ? $F[1] =~ s/-// : $F[1] =~ s/\d/-$&/; print join "|", #F' ip.txt
1 | -2.0
2 | 3.0
3 | -1.0
To modify multiple files inplace within a folder, use the -i option
sed -i -E '/^(.*\|\s*)-[0-9]/ {s/^(.*\|\s*)-/\1/;b}; s/^(.*\|\s*)/&-/' *
and
perl -i -F'\|' -lane '$F[1] =~ m/-/ ? $F[1] =~ s/-// : $F[1] =~ s/\d/-$&/; print join "|", #F' *
If number format is not an issue,
$ perl -F'\|' -lane '$F[1] = -$F[1]; print join "|", #F' ip.txt
1 |-2
2 |3
3 |-1
I want to add few in a file. Is it possible to use sed?
original file
test1
test2
test3
Expected output after adding the new line
test1
#
testing123
#
test3
If you don't mind using "while/read" instead of "sed", this is one solution:
[~]$ cat original.txt
test1
test2
test3
[~]$ cat new_content.txt
#
testing123
#
Then, process both files with the following script:
script.sh
#!/bin/bash
while IFS= read -r line
do
if [[ $line =~ ^test2.*$ ]]
then
cat new_content.txt
else
echo "$line"
fi
done < original.txt
sed '2 i\
\
#
2 a\
#\
' YourFile
Arbitrary take the line 2 as middle (not easy to count or take middle in posix sed)
ifor insert (before)
a for append (after)
I have some files in a directory as below (not necessarily sorted):
A_10
A_20
A_30
B_10
B_30
C_10
C_20
D_20
D_30
E_10
E_20
E_30
10, 20 and 30 are the sequence numbers of A,B,C,D,E respectively.
I want to select only those files with minimum sequence of all A,B,C,D,E
the output should be :
A_10
B_10
C_10
D_20
E_10
could anybody help me?
perl -le '
print join $/,
grep !$_{( split "_" )[0]}++,
sort glob "*_*"
'
or:
printf '%s\n' *_* | sort | awk -F_ '!_[$1]++'
or:
printf '%s\n' *_* | sort -t_ -uk1,1
In bash:
for x in A B C D E; do
ls -1 ${x}_* | sort | head -n1
done
How can I join a string from the left side to the output?
For example: we want to join parameter="file/"
remark: file=/dir1/dir2/ (file has a value)
echo aaa bbb | awk '{print $2}' | sed ....
Will print
/dir1/dir2/bbb
Assuming your input is good, this should be enough.
sed "s|\(.*\)|$VARIABLE\1|"
echo aaa bbb | awk '{print "file/"$2}'
How about:
echo aaa bbb | awk '{ print "file/" $2 }'