How do I use sed to remove decimal numbers from a string? - linux

I have the following string as an example:
ex. "Abandoned 16 1.10 2.62 3.50"
I would like to pipe this result to sed and remove all decimal numbers to leave me with the following:
ex. "Abandoned 16"
I was using the following command: sed 's/.//g'
which apparently doesn't work.
Can someone let me know how to use the wildcard character with sed to remove anything matching ".".
Thanks

You haven't said what you want to do with the whitespace, but how about
sed -e 's/[0-9]*\.[0-9]*//g' -e 's/ *$//'

this would be easier with awk, at least for me
echo "Abandoned 16 1.10 2.62 3.50" | awk '{print $1FS$2}'
but is the list of numbers random afterwards?
if so, this works too
echo "Abandoned 16 1.10 2.62 3.50" | sed -r 's/\s([0-9]+)\.([0-9]+)//g'
note that \s catches the white space, and that the numbers before and after the decimal are saved, so if you want to retain them and do something with them you can access them with \1 and \2 respectfully
Why catch the white sapce? well imagine if 16 came after 3.50 in your example you would then return
Abandoned [5spaces*] 16
*I can only insert one space in this <textarea>

Trowing in a awk solution that loops ovewr the input and skips entries with a period in them
{
printf("%s ", $1)
for(i=2;i<NF;i++) {
if ($i !~ /\./) {
printf( " %s ", $i)
}
}
}
$ echo Abandoned 16 1.10 2.62 3.50 | awk -f f.awk
Abandoned 16

Related

How to generate a random string without repeated characters?

Im trying to generate a password in Bash that matches MacOS password requirements and one of them is that it can't have repeated characters (aa, bb, 44, 00, etc).
I know i can use openssl rand -base64 or /dev/urandom and use tr -d to manipulate the output string. I use grep -E '(.)\1{1,}' to search for repeated characters but if i use this regex to delete (tr -d (.)\1{1,}'), it deletes the entire string. I even tried tr -s '(.)\1{1,}' to squeeze the characters to just one occurrence but it keep generating repeated characters in some attempts. Is it possible to achieve what i'm trying to?
P.S.: that's a situation where i cant download any "password generator tool" like pwgen and more. It must be "native"
Sorry I have no bash at hands to, but trying to help you.
What about iteratively grabbing the unique chars, eg. by
chars=$(openssl rand -base64)
pwd=
for (( i=0; i<${#chars}; i++ )); do
if [[ "$(echo $pwd | grep "${chars:$i:1}")" == "" ]]; then
pwd=$pwd${chars:$i:1}
fi
done
The issue might be that you have non-printable characters, so it's not actually repeated. If you first get the first e.g. 30 characters, then delete any non-alphanumeric, non punctuation characters, then squeeze any of those characters, then from whatever is left get the first 20 characters, it seems to work:
cat /dev/urandom | tr -dc '[:alnum:][:punct:]' | fold -w ${1:-30} | head -n 1 | tr -s '[:alnum:][:punct:]' | cut -c-20
Output e.g.:
]'Zc,fs6m;wUo%wLIG%K
2O3Ff4dzi30~L.RH8jR0
sU?,WkK]&I;z'|eTSLjY
5gK]\H51i#Rtux.{bdC=
:g"\?5JsjBd1r])2^WR+
;{cR:jY\rIc&Q(2yo:|-
fFykmxvZ|ATX_l6L(8h:
^Sd*,V%9}bWnTYNv"w?'
6foMgbU6:n<*cWj2W=3&
*v39FWmB#LwE5O`a3C36
Is there a specific size requirement? Other required characters?
How about -
openssl rand -base64 20 | sed -E 's/(.)\1+/\1/g'
You're getting close. tr doesn't use regex (but does use POSIX character classes).
Either of these will squeeze repeats:
tr -cs '\0'
tr -s '[:graph:][:space:]'
They differ only in how we refer to "all characters". First is "complement of null" second is all printable and all white space characters. There may be a neater way to specify "all characters".
Or using sed:
sed -E 's/(.)\1+/\1/g'
This both squeezes printable characters, and removes white space:
tr -ds '[:space:]' '[:graph:]'
Example for 32 non whitespace characters, with no repeats:
tr -ds '[:space:]' '[:graph:]' < /dev/urandom |
dd bs=32 count=1
Also, this example specifies a list of allowed characters (letters, digits, and _.), then squeezes any repeats:
tr -dc '[:alnum:]_.' < /dev/urandom |
tr -sc '\0' |
dd bs=32 count=1
Example output:
9mCEqrhHPwmq7.1qEky6qn4jqzDpRK7b
Putting dd at the end means we get 32 characters after removing repeats. You may also want to add status=none to hide dd logging on stderr.
It's not clear if you don't want consecutive chars repeated or no repeated chars at all (which in either case I don't think it's a good idea as it would make your passwords weaker and easier to guess), but having said that
#! /bin/bash
awk -vN=20 '
{
n=split($0,ch,"");
for (i=1; i<n; i++) {
a[ch[i]]++
}
n=0;
for (c in a) {
if (++n > N) {
break;
}
printf("%c",c)
}
printf("\n")
}
' < <(openssl rand -base64 32)
this generated N length passwords without repeated chars from 32 random bytes (that is N should be much smaller than 32)

Trim a string up to 4th delimiter from right side

I have strings like following which should be parsed with only unix command (bash)
49_sftp_mac_myfile_simul_test_9999_4000000000000001_2017-02-06_15-15-26.49.csv.failed
I want to trim the strings like above upto 4th underscore from end/right side. So output should be
49_sftp_mac_myfile_simul_test
Number of underscores can vary in overall string. For example, The string could be
49_sftp_simul_test_9999_4000000000000001_2017-02-06_15-15-26.49.csv.failed
Output should be (after trimming up to 4th occurrence of underscore from right.
49_sftp_simul_test
Easily done using awk that decrements NF i.e. no. of fields to -4 after setting input+output field separator as underscore:
s='49_sftp_mac_myfile_simul_test_9999_4000000000000001_2017-02-06_15-15-26.49.csv.failed'
awk 'BEGIN{FS=OFS="_"} {NF -= 4; $1=$1} 1' <<< "$s"
49_sftp_mac_myfile_simul_test
You can use bash's parameter expansion for that:
string="..."
echo "${string%_*_*_*_*}"
With GNU sed:
$ sed -E 's/(_[^_]*){4}$//' <<< "49_sftp_mac_myfile_simul_test_9999_4000000000000001_2017-02-06_15-15-26.49.csv.failed"
49_sftp_mac_myfile_simul_test
From the end of line, removes 4 occurrences of _ followed by non _ characters.
Perl one-liner
echo $your-string | perl -lne '$n++ while /_/g; print join "_",((split/_/)[-$n-1..-5])'
input
49_sftp_mac_myfile_simul_test_9999_4000000000000001_2017-02-06_15-15-26.49.csv.failed
the output
49_sftp_mac_myfile_simul_test
input
49_sftp_simul_test_9999_4000000000000001_2017-02-06_15-15-26.49.csv.failed
the output
49_sftp_simul_test
Not the fastest but maybe the easiest to remember and funiest:
echo "49_sftp_mac_myfile_simul_test_9999_4000000000000001_2017-02-06_15-15-26.49.csv.failed"|
rev | cut -d"_" -f5- | rev

Grabbing data between two commas

I am in the process of writing a simple script to grab battery information from acpi so I can format and output it.
Currently, I am using cut to grab this information, but as the battery state changes, cut does not grab the correct data and instead will grab a portion of a string instead of the battery percentage.
When running acpi -b, I get the following output:
Battery 0: Unknown, 100
Occasionally, acpi -b will also return the following, or something similar if it is charging or discharging:
Battery 0: Discharging, 98%, 02:14:14 remaining
So, without using cut, I'd like to be able to grab the data after the first comma, and, on occasion when present, grab the information between both commas. Right now, I am using sed to strip whitespace and the percentage sign from the output. Here is that command:
acpi -b | cut -c20-24 | sed 's/ //g;s/%//g'
You can use use this simple awk command:
s='Battery 0: Unknown, 100'
awk -F',[[:blank:]]*' '{sub(/%/, "", $2); print $2}' <<< "$s"
100
s='Battery 0: Discharging, 98%, 02:14:14 remaining'
awk -F',[[:blank:]]*' '{sub(/%/, "", $2); print $2}' <<< "$s"
98
awk breakup:
-F,[[:blank:]]* # makes comma followed by 0 or more spaces as field separator
sub(/%/, "", $2) # remove trailing %
print $2 # print 2nd field
With sed:
$ str1='Battery 0: Unknown, 100'
$ str2='Battery 0: Discharging, 98%, 02:14:14 remaining'
$ sed 's/^[^,]*, \([0-9]*\).*$/\1/' <<< "$str1"
100
$ sed 's/^[^,]*, \([0-9]*\).*$/\1/' <<< "$str2"
98
The substitution matches everything up to the first comma and a space (^[^,]*,), then captures any sequence of numbers (\([0-9]*\)) and matches the rest of the line (.*$), then substitutes the whole line with the captured numbers.

Linux/awk convert file containing decimal to hex

I have a text file containing lots of RGB colour codes in decimal. For example
000,000,000
000,003,025
000,007,048
000,010,069
000,014,089
000,017,108
000,020,125
000,024,140
000,027,155
I would like to convert each line to hex format (desired output):
00,00,00
00,03,15
00,07,30
00,08,45
I know I can use printf "%.2x,%.2x,%.2x\n" 000 010 69 however printf "%.2x,%.2x,%.2x\n" 000 010 069 does not work as 069 is not convertable.
I thought awk would be a reasonable tool for the job, but I guess I would face the same problems converting decimals such as 069 etc.
perl -le '$hex = sprintf("%.2x,%.2x,%.2x",005,69,255); print $hex' also has the same issue with 069
It works fine in awk:
$ echo 000,062,102 | awk '{printf( "%x,%x,%x\n", $1,$2,$3)}' FS=,
0,3e,66
You're simply missing the commas between the arguments:
echo "000,010,069" | awk -F ',' '{ printf "%02X,%02X,%02X\n", $1, $2, $3 }'
produces:
00,0A,45
Verified both on Mac OS X (BSD awk) and Linux (GNU awk).
Perl solution:
perl -pe 's/([0-9]+)/sprintf "%02x", $1/ge' INPUT
You do not have to care about octal interpretation. It applies to literals only, not to values of variables.

Filtering Linux command output

I need to get a row based on column value just like querying a database. I have a command output like this,
Name ID Mem VCPUs State
Time(s)
Domain-0 0 15485 16 r-----
1779042.1
prime95-01 512 1
-b---- 61.9
Here I need to list only those rows where state is "r". Something like this,
Domain-0 0 15485 16
r----- 1779042.1
I have tried using "grep" and "awk" but still I am not able to succeed.
Any help me is much appreciated
Regards,
Raaj
There is a variaty of tools available for filtering.
If you only want lines with "r-----" grep is more than enough:
command | grep "r-----"
Or
cat filename | grep "r-----"
grep can handle this for you:
yourcommand | grep -- 'r-----'
It's often useful to save the (full) output to a file to analyse later. For this I use tee.
yourcommand | tee somefile | grep 'r-----'
If you want to find the line containing "-b----" a little later on without re-running yourcommand, you can just use:
grep -- '-b----' somefile
No need for cat here!
I recommend putting -- after your call to grep since your patterns contain minus-signs and if the minus-sign is at the beginning of the pattern, this would look like an option argument to grep rather than a part of the pattern.
try:
awk '$5 ~ /^r.*/ { print }'
Like this:
cat file | awk '$5 ~ /^r.*/ { print }'
grep solution:
command | grep -E "^([^ ]+ ){4}r"
What this does (-E switches on extended regexp):
The first caret (^) matches the beginning of the line.
[^ ] matches exactly one occurence of a non-space character, the following modifier (+) allows it to also match more occurences.
Grouped together with the trailing space in ([^ ]+ ), it matches any sequence of non-space characters followed by a single space. The modifyer {4} requires this construct to be matched exactly four times.
The single "r" is then the literal character you are searching for.
In plain words this could be written like "If the line starts <^> with four strings that are followed by a space <([^ ]+ ){4}> and the next character is , then the line matches."
A very good introduction into regular expressions has been written by Jan Goyvaerts (http://www.regular-expressions.info/quickstart.html).
Filtering by awk cmd in linux:-
Firstly find the column for this cmd and store file2 :-
awk '/Domain-0 0 15485 /' file1 >file2
Output:-
Domain-0 0 15485 16
r----- 1779042.1
after that awk cmd in file2:-
awk '{print $1,$2,$3,$4,"\n",$5,$6}' file2
Final Output:-
Domain-0 0 15485 16
r----- 1779042.1

Resources