Manipulating the output string in bash shell - bash
I have written a line that finds and returns the full path to a desired file. The output is as follows:
/home/ke/Desktop/b/o/r/files.txt:am.torrent
/home/ke/Desktop/y/u/n/u/s/files.txt:asd.torrent
I have to modify the output like this:
bor
yunus
How do I do that?
Thanks in advance.
This should work for you:
your_script.sh | sed 's,.*Desktop,,' | sed 's,[^/]*$,,' | sed s,/,,g
or, even better:
your_script.sh | sed 's,.*Desktop,,;s,[^/]*$,,;s,/,,g'
With sed. echo '/home/ke/Desktop/b/o/r/files.txt:am.torrent' | sed -e 's+/++g' -e 's/^.*Desktop//' -e 's/files.txt:.*$//'. This is a fairly trivial solution, and I'm sure there are better ones.
Id resort to awk:
BEGIN { FS="/" }
{
for(i=1;i<NF;i++)
if (length($i) == 1)
a[NR]=a[NR]""$i
}
END {
for (i in a)
print a[i]
}
use it like this:
$ awk -f script.awk input
bor
yunus
or if you have your data in a variable:
$ awk -f script.awk <<< $data
it's not a nice/tidy solution, but bash parameter expansion is a powerful tool. So could not resist providing an example
[]l="/home/ke/Desktop/b/o/r/files.txt:am.torrent"
[]m=${l##*Desktop/}
[]n=${m%%/files.txt*}
[]k=${n//\//}
[]echo $m
b/o/r/files.txt:am.torrent
[]echo $n
b/o/r
[]echo $k
bor
You can see how nicely bash is replacing the variable step by step without using any external program (btw [] is PS1, prompt)
There can be many more ways to do it. I got another one while writing the first
[]l="/home/ke/Desktop/b/o/r/files.txt:am.torrent"
[]m=${l/*Desktop\//}
[]n=${m/\/files.txt*/}
[]k=${n//\//}
[]echo $m
b/o/r/files.txt:am.torrent
[]echo $n
b/o/r
[]echo $k
bor
Try some more,
Related
shell script compare file with multiple line pattern
I have a file which is created after some manual configuration. I need to check this file automatically with a shell script. The file looks like this: eth0;eth0;1c:98:ec:2a:1a:4c eth1;eth1;1c:98:ec:2a:1a:4d eth2;eth2;1c:98:ec:2a:1a:4e eth3;eth3;1c:98:ec:2a:1a:4f eth4;eth4;48:df:37:58:da:44 eth5;eth5;48:df:37:58:da:45 eth6;eth6;48:df:37:58:da:46 eth7;eth7;48:df:37:58:da:47 I want to compare it to a pattern like this: eth0;eth0;* eth1;eth1;* eth2;eth2;* eth3;eth3;* eth4;eth4;* eth5;eth5;* eth6;eth6;* eth7;eth7;* If I would only have to check this pattern I could run this loop: c=0 while [ $c -le 7 ] do if [ "$(grep "eth"${c}";eth"${c}";*" current_mapping)" ]; then echo "eth$c ok" fi (( c++ )) done There are 6 or more different patterns possible. A pattern could also look like this for example (depending and specific configuration requests): eth4;eth0;* eth5;eth1;* eth6;eth2;* eth7;eth3;* eth0;eth4;* eth1;eth5;* eth2;eth6;* eth3;eth7;* So I don't think I can run a standard grep per line command in a loop. The eth numbers are not consistently the same. Is it possible somehow to compare the whole file to pattern like it would be possible with grep for a single line?
Assuming file is your data file and patt is your file that contains above pattern. You can use this grep -f in conjunction with sed in a process substitution that replaces * with .* and ? with . to make it a workable regex. grep -f <(sed 's/\*/.*/g; s/?/./g' patt) file eth0;eth0;1c:98:ec:2a:1a:4c eth1;eth1;1c:98:ec:2a:1a:4d eth2;eth2;1c:98:ec:2a:1a:4e eth3;eth3;1c:98:ec:2a:1a:4f eth4;eth4;48:df:37:58:da:44 eth5;eth5;48:df:37:58:da:45 eth6;eth6;48:df:37:58:da:46 eth7;eth7;48:df:37:58:da:47
I wrote this loop now and it does the job (current_mapping being the file with the content in the first code block of the question). I would have to create arrays with different patterns and use a case for every pattern. I was just wondering if there is something like grep for multiple lines, that could the same without writing this loop. array=("eth0;eth0;*" "eth1;eth1;*" "eth2;eth2;*" "eth3;eth3;*" "eth4;eth4;*" "eth5;eth5;*" "eth6;eth6;*" "eth7;eth7;*") c=1 while [ $c -le 8 ] do if [ ! "$(sed -n "${c}"p current_mapping | grep "${array[$c-1]}")" ]; then echo "somethings wrong" fi (( c++ )) done
Try any: grep -P '(eth[0-9]);\1' grep -E '(eth[0-9]);\1' sed -n '/\(eth[0-9]\);\1/p' awk -F';' '$1 == $2' There are commands only. Apply them to a pipe or file. Updated the answer after the question was edited. As we can see the task requirements are as follows: a file (a set of lines) formatted like ethN;ethM;MAC examine each line for equality ethN and ethM if they are equal, output a string ethN ok If I understand the task correctly we can achieve this using the following code without loops: awk -F';' '$1 == $2 { print $1, "ok" }'
Wrong search result in a file through Bash script
I am searching an event field in a file but is giving wrong output. I am searching gpio-keys event in input devices for which I have written a script, but I'm unable to print anything in output file (in my case I am writing in a button device file it is null always). Please help me to figure out this. Where am I doing wrong in script file? Bash script: #!/bin/bash if grep -q "gpio-keys" /proc/bus/input/devices ; then EVENT=$(cat /proc/bus/input/devices | grep "Handlers=kbd") foo= `echo $EVENT | awk '{for(i=1;i<=NF;i++) if($i=="evbug")printf($(i-1))}'` #foo=${EVENT:(-7)} echo -n $foo > /home/ubuntu/Setups/buttonDevice fi
i am still not able to get anything in buttondevce That's no wonder, since in the input line H: Handlers=kbd event0 there's nowhere the evbug your awk script is looking for. I my case it is event0 but it may vary also depends on how kernel allows. If it is event0 or similar, then it's nonsensical to look for evbug. Change the statement if($i=="evbug")printf($(i-1)) to if ($i~"event") print $i (using regular expression match). I have rewritten my script like above. but through it, I have got two events(event0, event3) but … my input devices are many but i want the gpio-keys event Aha - in order to take only the handler line from the gpio-keys section, you can use sed with an address range: EVENT=`sed -n '/gpio-keys/,/Handlers=kbd/s/.*Handlers=kbd //p' </proc/bus/input/devices`
Prakash, I don't have access to your google drive. But I just want to give you some suggestion:- foo= `echo $EVENT | awk '{for(i=1;i<=NF;i++) if($i=="evbug")printf($(i-1))}'` This is old style now. Better use like below:- foo=$(echo $EVENT | awk '{for(i=1;i<=NF;i++) if($i=="evbug")printf($(i-1))}') Also always use double quotes "" when echoing a variable. See below:- echo -n "$foo" > /home/ubuntu/Setups/buttonDevice Try with the below code it will work for you #!/bin/bash if grep "gpio-keys" /proc/bus/input/devices >/dev/null ; then cat /proc/bus/input/devices | grep "Handlers=kbd" | awk '{for(i=1;i<=NF;i++){ if($i ~ /eve/){printf "%s \n", $i} } }') > /home/ubuntu/Setups/buttonDevice fi The output in buttonDevice would be event0 event1 . . . . event100
Bash script for parsing and processing specific lines of a text file
I have the following text file 40 timesteps took 58.320842 seconds greetings 0 80 timesteps took 58.048400 seconds greetings 0 120 timesteps took 59.459687 seconds greetings 0 What I would like to do is parse only the lines containing the seconds, add them together and print out the final result. How can I do that? Thank you in advance.
awk is well suited for this type of processing. To deal with floating point precision, you can use printf with a format-string for each variable involved. There is also another way which sets the format-string for all evaluated variables. The formatting is applied during evaluations (which internally use sprintf. The controlling built-in variable is OFMT. See Built-in Variables That Control awk. #!/bin/bash file="$1" # $1 is the 1st command line parameter awk -vOFMT="%.6f" '/ took /{ secs+=$4 } END{ print secs }' "$file" Using sed is more involved, because it cannot do any calculations and even bash itself cannot do floating point arithmetic, so you need to use something like awk or bc in any case. If you really want to use sed: #!/bin/bash file="$1" # $1 is the 1st command line parameter { sed -nr 's/.* took ([0-9.]+).*/\1+/p' "$file" |tr -d '\n'; echo 0; } |bc
You can use a simple shell command: grep timesteps <file-name> | awk '{x += $4} END{printf("%.5f", x)}' Change the number in the printf statement to your preferred output precision.
The awk solutions are good answers. For fun, here is a Ruby answer... ruby -e 'puts readlines.inject(0) { |m, v| m += v.split[3].to_f }' < file ...or perhaps... ruby -e 'puts readlines.map { |x| x.split[3].to_f }.reduce(&:+)' < file ...to pass the file as a parameter to a script... #!/usr/bin/ruby puts $<.map { |x| x.split[3].to_f }.reduce(&:+)
How to convert HHMMSS to HH:MM:SS Unix?
I tried to convert the HHMMSS to HH:MM:SS and I am able to convert it successfully but my script takes 2 hours to complete because of the file size. Is there any better way (fastest way) to complete this task Data File data.txt 10,SRI,AA,20091210,8503,ABCXYZ,D,N,TMP,,, 10,SRI,AA,20091210,8503,ABCXYZ,D,N,TMP,,071600, 10,SRI,AA,20091210,8503,ABCXYZ,D,N,TMP,072200,072200, 10,SRI,AA,20091210,8503,ABCXYZ,D,N,TAB,072600,072600, 10,SRI,AA,20091210,8503,ABCXYZ,D,N,TMP,073200,073200, 10,SRI,AA,20091210,8503,ABCXYZ,D,N,TMP,073500,073500, 10,SRI,AA,20091210,8503,ABCXYZ,D,N,MRO,073700,073700, 10,SRI,AA,20091210,8503,ABCXYZ,D,N,CPT,073900,073900, 10,SRI,AA,20091210,8503,ABCXYZ,D,N,TMP,074400,, 10,SRI,AA,20091210,8505,ABCXYZ,D,N,TMP,,, 10,SRI,AA,20091210,8505,ABCXYZ,D,N,TMP,,090200, 10,SRI,AA,20091210,8505,ABCXYZ,D,N,TMP,090900,090900, 10,SRI,AA,20091210,8505,ABCXYZ,D,N,TMP,091500,091500, 10,SRI,AA,20091210,8505,ABCXYZ,D,N,TAB,091900,091900, 10,SRI,AA,20091210,8505,ABCXYZ,D,N,TMP,092500,092500, 10,SRI,AA,20091210,8505,ABCXYZ,D,N,TMP,092900,092900, 10,SRI,AA,20091210,8505,ABCXYZ,D,N,MRO,093200,093200, 10,SRI,AA,20091210,8505,ABCXYZ,D,N,CPT,093500,093500, 10,SRI,AA,20091210,8505,ABCXYZ,D,N,TMP,094500,, 10,SRI,AA,20091210,8506,ABCXYZ,U,N,TMP,,, 10,SRI,AA,20091210,8506,ABCXYZ,U,N,CPT,,, 10,SRI,AA,20091210,8506,ABCXYZ,U,N,MRO,,, 10,SRI,AA,20091210,8506,ABCXYZ,U,N,TMP,,, 10,SRI,AA,20091210,8506,ABCXYZ,U,N,TMP,,, 10,SRI,AA,20091210,8506,ABCXYZ,U,N,TAB,,, 10,SRI,AA,20091210,8506,ABCXYZ,U,N,TMP,,, 10,SRI,AA,20091210,8506,ABCXYZ,U,N,TMP,,, 10,SRI,AA,20091210,8506,ABCXYZ,U,N,TMP,,, 10,SRI,AA,20091210,8506,ABCXYZ,U,N,TMP,,, 10,SRI,AA,20091210,8510,ABCXYZ,U,N,TMP,,170100, 10,SRI,AA,20091210,8510,ABCXYZ,U,N,CPT,170400,170400, 10,SRI,AA,20091210,8510,ABCXYZ,U,N,MRO,170700,170700, 10,SRI,AA,20091210,8510,ABCXYZ,U,N,TMP,171000,171000, 10,SRI,AA,20091210,8510,ABCXYZ,U,N,TMP,171500,171500, 10,SRI,AA,20091210,8510,ABCXYZ,U,N,TAB,171900,171900, 10,SRI,AA,20091210,8510,ABCXYZ,U,N,TMP,172500,172500, 10,SRI,AA,20091210,8510,ABCXYZ,U,N,TMP,172900,172900, 10,SRI,AA,20091210,8510,ABCXYZ,U,N,TMP,173500,173500, 10,SRI,AA,20091210,8510,ABCXYZ,U,N,TMP,174100,, My code : script.sh #!/bin/bash awk -F"," '{print $5}' Data.txt > tmp.txt # print first line first string before , to tmp.txt i.e. all Numbers will be placed into tmp.txt sort tmp.txt | uniq -d > Uniqe_number.txt # unique values be stored to Uniqe_number.txt rm tmp.txt # removes tmp file while read line; do echo $line cat Data.txt | grep ",$line," > Numbers/All/$line.txt # grep Number and creats files induvidtually awk -F"," '{print $5","$4","$7","$8","$9","$10","$11}' Numbers/All/$line.txt > Numbers/All/tmp_$line.txt mv Numbers/All/tmp_$line.txt Numbers/Final/Final_$line.txt done < Uniqe_number.txt ls Numbers/Final > files.txt dos2unix files.txt bash time_replace.sh when you execute above script it will call time_replace.sh script My Code for time_replace.sh #!/bin/bash for i in `cat files.txt` do while read aline do TimeDep=`echo $aline | awk -F"," '{print $6}'` #echo $TimeDep finalTimeDep=`echo $TimeDep | awk '{for(i=1;i<=length($0);i+=2){printf("%s:",substr($0,i,2))}}'|awk '{sub(/:$/,"")};1'` #echo $finalTimeDep ########## TimeAri=`echo $aline | awk -F"," '{print $7}'` #echo $TimeAri finalTimeAri=`echo $TimeAri | awk '{for(i=1;i<=length($0);i+=2){printf("%s:",substr($0,i,2))}}'|awk '{sub(/:$/,"")};1'` #echo $finalTimeAri sed -i 's/',$TimeDep'/',$finalTimeDep'/g' Numbers/Final/$i sed -i 's/',$TimeAri'/',$finalTimeAri'/g' Numbers/Final/$i ############################ done < Numbers/Final/$i done Any better solution? Appreciate any help. Thanks Sri
If there's a large quantity of files, then the pipelines are probably what are going to impact performance more than anything else - although processes can be cheap, if you're doing a huge amount of processing then cutting down the amount of time you do pass data through a pipeline can reap dividends. So you're probably going to be better off writing the entire script in awk (or perl). For example, awk can send output to an arbitary file, so the while lop in your first file could be replaced with an awk script that does this. You also don't need to use a temporary file. I assume the sorting is just for tracking progress easily as you know how many numbers there are. But if you don't care for the sorting, you can simply do this: #!/bin/sh awk -F ',' ' { print $5","$4","$7","$8","$9","$10","$11 > Numbers/Final/Final_$line.txt }' datafile.txt ls Numbers/Final > files.txt Alternatively, if you need to sort you can do sort -t, -k5,4,10 (or whichever field your sort keys actually need to be). As for formatting the datetime, awk also does functions, so you could actually have an awk script that looks like this. This would replace both of your scripts above whilst retaining the same functionality (at least, as far as I can make out with a quick analysis) ... (Note! Untested, so may contain vauge syntax errors): #!/usr/bin/awk BEGIN { FS="," } function formattime (t) { return substr(t,1,2)":"substr(t,3,2)":"substr(t,5,2) } { print $5","$4","$7","$8","$9","formattime($10)","formattime($11) > Numbers/Final/Final_$line.txt } which you can save, chmod 700, and call directly as: dostuff.awk filename Other awk options include changing fields in-situ, so if you want to maintain the entire original file but with formatted datetimes, you can do a modification of the above. Change the print block to: { $10=formattime($10) $11=formattime($11) print $0 } If this doesn't do everything you need it to, hopefully it gives some ideas that will help the code.
It's not clear what all your sorting and uniq-ing is for. I'm assuming your data file has only one entry per line, and you need to change the 10th and 11th comma-separated fields from HHMMSS to HH:MM:SS. while IFS=, read -a line ; do echo -n ${line[0]},${line[1]},${line[2]},${line[3]}, echo -n ${line[4]},${line[5]},${line[6]},${line[7]}, echo -n ${line[8]},${line[9]}, if [ -n "${line[10]}" ]; then echo -n ${line[10]:0:2}:${line[10]:2:2}:${line[10]:4:2} fi echo -n , if [ -n "${line[11]}" ]; then echo -n ${line[11]:0:2}:${line[11]:2:2}:${line[11]:4:2} fi echo "" done < data.txt The operative part is the ${variable:offset:length} construct that lets you extract substrings out of a variable.
In Perl, that's close to child's play: #!/usr/bin/env perl use strict; use warnings; use English( -no_match_vars ); local($OFS) = ","; while (<>) { my(#F) = split /,/; $F[9] =~ s/(\d\d)(\d\d)(\d\d)/$1:$2:$3/ if defined $F[9]; $F[10] =~ s/(\d\d)(\d\d)(\d\d)/$1:$2:$3/ if defined $F[10]; print #F; } If you don't want to use English, you can write local($,) = ","; instead; it controls the output field separator, choosing to use comma. The code reads each line in the file, splits it up on the commas, takes the last two fields, counting from zero, and (if they're not empty) inserts colons in between the pairs of digits. I'm sure a 'Code Golf' solution would be made a lot shorter, but this is semi-legible if you know any Perl. This will be quicker by far than the script, not least because it doesn't have to sort anything, but also because all the processing is done in a single process in a single pass through the file. Running multiple processes per line of input, as in your code, is a performance disaster when the files are big. The output on the sample data you gave is: 10,SRI,AA,20091210,8503,ABCXYZ,D,N,TMP,,, 10,SRI,AA,20091210,8503,ABCXYZ,D,N,TMP,,07:16:00, 10,SRI,AA,20091210,8503,ABCXYZ,D,N,TMP,07:22:00,07:22:00, 10,SRI,AA,20091210,8503,ABCXYZ,D,N,TAB,07:26:00,07:26:00, 10,SRI,AA,20091210,8503,ABCXYZ,D,N,TMP,07:32:00,07:32:00, 10,SRI,AA,20091210,8503,ABCXYZ,D,N,TMP,07:35:00,07:35:00, 10,SRI,AA,20091210,8503,ABCXYZ,D,N,MRO,07:37:00,07:37:00, 10,SRI,AA,20091210,8503,ABCXYZ,D,N,CPT,07:39:00,07:39:00, 10,SRI,AA,20091210,8503,ABCXYZ,D,N,TMP,07:44:00,, 10,SRI,AA,20091210,8505,ABCXYZ,D,N,TMP,,, 10,SRI,AA,20091210,8505,ABCXYZ,D,N,TMP,,09:02:00, 10,SRI,AA,20091210,8505,ABCXYZ,D,N,TMP,09:09:00,09:09:00, 10,SRI,AA,20091210,8505,ABCXYZ,D,N,TMP,09:15:00,09:15:00, 10,SRI,AA,20091210,8505,ABCXYZ,D,N,TAB,09:19:00,09:19:00, 10,SRI,AA,20091210,8505,ABCXYZ,D,N,TMP,09:25:00,09:25:00, 10,SRI,AA,20091210,8505,ABCXYZ,D,N,TMP,09:29:00,09:29:00, 10,SRI,AA,20091210,8505,ABCXYZ,D,N,MRO,09:32:00,09:32:00, 10,SRI,AA,20091210,8505,ABCXYZ,D,N,CPT,09:35:00,09:35:00, 10,SRI,AA,20091210,8505,ABCXYZ,D,N,TMP,09:45:00,, 10,SRI,AA,20091210,8506,ABCXYZ,U,N,TMP,,, 10,SRI,AA,20091210,8506,ABCXYZ,U,N,CPT,,, 10,SRI,AA,20091210,8506,ABCXYZ,U,N,MRO,,, 10,SRI,AA,20091210,8506,ABCXYZ,U,N,TMP,,, 10,SRI,AA,20091210,8506,ABCXYZ,U,N,TMP,,, 10,SRI,AA,20091210,8506,ABCXYZ,U,N,TAB,,, 10,SRI,AA,20091210,8506,ABCXYZ,U,N,TMP,,, 10,SRI,AA,20091210,8506,ABCXYZ,U,N,TMP,,, 10,SRI,AA,20091210,8506,ABCXYZ,U,N,TMP,,, 10,SRI,AA,20091210,8506,ABCXYZ,U,N,TMP,,, 10,SRI,AA,20091210,8510,ABCXYZ,U,N,TMP,,17:01:00, 10,SRI,AA,20091210,8510,ABCXYZ,U,N,CPT,17:04:00,17:04:00, 10,SRI,AA,20091210,8510,ABCXYZ,U,N,MRO,17:07:00,17:07:00, 10,SRI,AA,20091210,8510,ABCXYZ,U,N,TMP,17:10:00,17:10:00, 10,SRI,AA,20091210,8510,ABCXYZ,U,N,TMP,17:15:00,17:15:00, 10,SRI,AA,20091210,8510,ABCXYZ,U,N,TAB,17:19:00,17:19:00, 10,SRI,AA,20091210,8510,ABCXYZ,U,N,TMP,17:25:00,17:25:00, 10,SRI,AA,20091210,8510,ABCXYZ,U,N,TMP,17:29:00,17:29:00, 10,SRI,AA,20091210,8510,ABCXYZ,U,N,TMP,17:35:00,17:35:00, 10,SRI,AA,20091210,8510,ABCXYZ,U,N,TMP,17:41:00,,
What is the most compact or efficient way of doing several subsitutions in a file in bash
I have a file data.base which looks like: 1234 XXXX 4321 XXXX 9884 ZZZZ 5454 YYYY 4311 YYYY 9882 ZZZZ 9976 ZZZZ ( ... random occurrences like this till 10000 lines) I would like to create a file called data.case which derives from data.base just with substitutions of XXXX, YYYY, ZZZZ for float numbers. I wonder what would be the most compact/efficient/short way to do that on bash or friends. What I usually do is something like: sed -e "s/XXXX/1.34555/g" data.base > temp1 sed -e "s/YYYY/2.985/g" temp1 > temp2 sed -e "s/ZZZZ/-4.3435/g" temp2 > data.case rm -fr temp1 temp2 But I do not think this is the most compact or efficient way when you have to deal with more than 3 substitutions. Thanks Thanks
Use an option to ececute several commands in same sed: sed "s/XXXX/1.34555/g; s/YYYY/2.985/g"; s/ZZZZ/-4.3435/g" data.base > data.case
$ cat sedcommands s/XXXX/1.34555/g s/YYYY/2.985/g s/ZZZZ/-4.3435/g $ sed -f sedcommands data.base > data.case
you can make use of associative arrays in awk awk 'BEGIN{ # add as needed s["XXXX"]=1.3455 s["YYYY"]=2.985 s["ZZZZ"]=-4.3435 } ($2 in s) { print $1,s[$2] }' file output $ ./shell.sh 1234 1.3455 4321 1.3455 9884 -4.3435 5454 2.985 4311 2.985 9882 -4.3435 9976 -4.3435
sed -e "s/XXXX/1.34555/g;s/YYYY/2.985/g;s/ZZZZ/-4.3435/g" or put them in a cmd file and list them out.
Whilst sed can do multiple substitutions in one pass, the general UNIX approach which is more widely applicable and can be combined with other commands is to use command piping: cat data.base | \ sed -e "s/XXXX/1.34555/g" | \ sed -e "s/YYYY/2.985/g" | \ sed -e "s/ZZZZ/-4.3435/g" > data.base The redirection at the end will 'unlink' the old data.base that is being used as input by cat; you could however still use a temporary file so that you can intercept error conditions and not have lost the original data.base in the process. (When using piping, its useful to be familiar with the tee program, which saves the stream to a file whilst passing it on)