I need to get only first two numbers of "uname -r" command in bash
example of regular out put:
uname -r
3.5.0-18-generic
what I expect using magic bash options:
3.5
assuming you want everything before the second dot, this will do what you want:
uname -r | cut -d. -f1-2
uname itself does not support cutting the output, afaik. The pipe through cut will show you fields 1 and 2 (-f1-2), delimited by dots (-d.)
uname -r | sed 's/\([0-9]\+\.[0-9]\+\)\..*/\1/'
You could also accomplish this with parameter expansion:
$ r="$(uname -r)"
$ echo ${r%.*}
3.5
${VAR%pat} non-greedily removes pat from the end of VAR. Note that pat is a glob pattern i.e. dot just means "dot" and star means "any-number-of-chars".
Related
I'm having trouble understanding why there is a difference in the outputs of these 3 commands when executed on different distributions
Could you guys help me understand why ?
printf "%s" `echo ../.. | sed 's/[.]/\\&/g'`
output on kali: &&/&&~ output on ubuntu: &&/&&
printf "%s" $(echo ../.. | sed 's/[.]/\\&/g')
output on kali: \.\./\.\. output on ubuntu: ../..
printf "%s" "$(echo ../.. | sed 's/[.]/\\&/g')"
output on kali: \.\./\.\. output on ubuntu: \.\./\.\.
Kali uses Zsh shell. Ubuntu uses Bash shell.
output on ubuntu: &&/&&
Do not use backticks - prefer $(...). Backticks remove \ before execution.
printf "%s" `echo ../.. | sed 's/[.]/\\&/g'`
becomes:
printf "%s" $(echo ../.. | sed 's/[.]/\&/g')
So replaces . by & character.
output on kali: &&/&&~
The ~ on the end in Zsh in your configuration represents output from a command without a newline.
output on ...: \.\./\.\.
That should be understandable.
output on ubuntu: ../..
Aaaand this is the hard one around Bash 5.0. Unquoted result of command substitution undergo filename expansion (also called pathname expansion). The \.\./\.\. matches the directory path ../... Bash 5.0 introduced a change that would make the result of expansion undergo pathname expansion even if it doesn't have any globbing special characters.
The change was reversed in Bash 5.1. Bash-5.1-alpha available for download https://lists.gnu.org/archive/html/bug-bash/2019-01/msg00063.html.
I need to remove any letters that occur after the first comma in a line
some.file
JAN,334X,333B,337A,338D,332Q,335H,331U
Expected Result:
JAN,334,333,337,338,332,335,331
Code:
sed -i 's/\[0-9][0-9][0-9].*,/[0-9][0-9][0-9],/g' some.file
What am I doing wrong?
You could also use a small loop (this is GNU sed);
sed ':;s/[A-Z],/,/2;t;s/[A-Z]$//'
It only deletes the second letter preceding a comma, and loops. Finally, it deletes the letter at the line's end, if there is one.
Try this
$ sed 's/,\([0-9]*\)[^,]*/,\1/g' <<<'JAN,334X,333B,337A,338D,332Q,335H,331U'
JAN,334,333,337,338,332,335,331
You need to capture the digits with round parenthesis in order to use the captured string in the replacement. The option g does this for every occurrence.
Comparison of the different answers
Test data:
$ > data; for ((x=1000000;x>0;x--)); do echo 'JAN,334X,333B,337A,338D,332Q,335H,331U' >> data; done
My answer is the slowest:
$ time sed 's/,\([0-9]*\)[^,]*/,\1/g' < data >/dev/null
real 0m16.368s
user 0m16.296s
sys 0m0.024s
Michael is a bit faster:
$ time sed ':;s/[A-Z],/,/2;t;s/[A-Z]$//' < data >/dev/null
real 0m9.669s
user 0m9.624s
sys 0m0.012s
But Sundeep is the fastet:
$ time sed 's/[A-Z]//4g' < data >/dev/null
real 0m4.905s
user 0m4.856s
sys 0m0.028s
Some issues are:
No need to escape [.
Your replace value is wrong. Ex: s/regex/replace/g
Use this:
sed -e 's/\([0-9]\+\)[a-zA-Z],/\1,/g' -e 's/\([0-9]\+\)[a-zA-Z]$/\1/g' file
You should omit the * and the first \ looks like a mistake i.e.
sed -i 's/[0-9][0-9][0-9].,/[0-9][0-9][0-9],/g' some.file
but I think you also want to capture the number ...
sed -i 's/\([0-9][0-9][0-9]\).,/\1,/g' some.file
Would be helpful if you posted your actual output as well ...
Since question is tagged linux, this GNU sed option comes in handy
$ echo 'JAN,334X,333B,337A,338D,332Q,335H,331U' | sed -E 's/[A-Z](,|$)/\1/2g'
JAN,334,333,337,338,332,335,331
2g means replace from 2nd match onwards till end of line
If number of letters is known for first column, this can be simplified to
$ echo 'JAN,334X,333B,337A,338D,332Q,335H,331U' | sed 's/[A-Z]//4g'
JAN,334,333,337,338,332,335,331
No need for sed, coreutils will do:
paste -d, <(cut -d, -f1 data) <(cut -d, -f2- data | tr -d 'A-Z')
This takes .3 seconds on my computer when run on the data file generated in ceving's answer.
I am new to bash and writing a script that needs to compare the minor version of the kernel to see if it is greater than or equal to 10, and exit if it is not. Currently I have something like this:
KERNEL=$(uname -r)
declare -i MINOR_VERSION=$(echo $KERNEL | cut -c 3-4)
if [ "10" -gt "$MINOR_VERSION"]; then exit 0; fi
This is bad code, and doesn't work if the minor version is < 10 since I am using cut and depending on it being two digits. What I probably need is something that parses the minor version by using the dots.
Example:
$ uname -r
3.4.0-60-generic
$ MNR_VAR=<awesome bash code, with cut or sed or something>
$ echo $MNR_VAR
4
I have been reading cut and sed documentation but have just been slow picking it up. I would appreciate the help!
TL;DR - looking for a bash command that will extract an int surrounded by the first two dots in a variable. "3.13.0.x" returns '13', "3.2.0.x" returns '2', etc.
EDIT:
Some answers as one liners below for those curious.
uname -r | cut -d '.' -f2
uname -r | awk -F . '{print $2}'
kernel="$(uname -r)" | tmp="${kernel#*.}" | minor="${tmp%%.*}" | echo "$minor"
In pure bash:
#!/bin/bash
ker="$(uname -r)"
minker="${ker#*.}"
minker="${minker%%.*}"
echo "$minker"
"${ker#*.}" is the string after the first match of a . in $ker. Thus
$minker becomes 13.0-generic... from 3.13.0-generic...
"${minker%%.*}" is the string left by cutting all matches (from right) of . and whatever after it, in $minker. Thus $minker becomes 13 from 13.0-generic...
See the Bash Parameter Expansion Manual for more info
Using Bash Regex:
#!/bin/bash
regex='([0-9]+)\.([0-9]+)'
[[ $(uname -r) =~ $regex ]]
echo ${BASH_REMATCH[2]}
The problem is you are using -c to cut. Don't do that.
Use the -f and -d flags instead to control the delimiter and fields to output.
Or use awk -F . '{print $2}' <<< "$(uname -r)".
Or use IFS=. read -r _ minor _rest <<< "$(uname -r)"; echo "$minor" (which has the benefit of not using any external utilities).
The usage of <<< "$(uname -r)" is bash-specific (I believe) but avoids the need for a pipe (|) and the sub-shell that it involves.
Extracting just minor version & comparing it with something is risky, because major number can change too...
I normally prefer padding the numbers with zeros, so that they can be easily compared using simple string compare.
kernel_version=$(uname -r | sed -r 's/([0-9]+)/0000\1/g; s/0*([0-9]{4})/\1/g') # gives 0003.0004.0000-0060-generic
if [[ "$kernel_version" < "0003.0010" ]]; then exit 0; fi
I'm trying to write a script with a file as an argument that greps the text file to find any word that starts with a capital and has 8 letters following it. I'm bad with syntax so I'll show you my code, I'm sure it's an easy fix.
grep -o '[A-Z][^ ]*' $1
I'm not sure how to specify that:
a) it starts with a capital letter, and
b)that it's a 9 letter word.
Cheers
EDIT:
As an edit I'd like to add my new code:
while read p
do
echo $p | grep -Eo '^[A-Z][[:alpha:]]{8}'
done < $1
I still can't get it to work, any help on my new code?
'[A-Z][^ ]*' will match one character between A and Z, followed by zero or more non-space characters. So it would match any A-Z character on its own.
Use \b to indicate a word boundary, and a quantifier inside braces, for example:
grep '\b[A-Z][a-z]\{8\}\b'
If you just did grep '[A-Z][a-z]\{8\}' that would match (for example) "aaaaHellosailor".
I use \{8\}, the braces need to be escaped unless you use grep -E, also known as egrep, which uses Extended Regular Expressions. Vanilla grep, that you are using, uses Basic Regular Expressions. Also note that \b is not part of the standard, but commonly supported.
If you use ^ at the beginning and $ at the end then it will not find "Wiltshire" in "A Wiltshire pig makes great sausages", it will only find lines which just consist of a 9 character pronoun and nothing else.
This works for me:
$ echo "one-Abcdefgh.foo" | grep -o -E '[A-Z][[:alpha:]]{8}'
$ echo "one-Abcdefghi.foo" | grep -o -E '[A-Z][[:alpha:]]{8}'
Abcdefghi
$
Note that this doesn't handle extensions or prefixes. If you want to FORCE the input to be a 9-letter capitalized word, we need to be more explicit:
$ echo "one-Abcdefghij.foo" | grep -o -E '\b[A-Z][[:alpha:]]{8}\b'
$ echo "Abcdefghij" | grep -o -E '\b[A-Z][[:alpha:]]{8}\b'
$ echo "Abcdefghi" | grep -o -E '\b[A-Z][[:alpha:]]{8}\b'
Abcdefghi
$
I have a test file named 'testfile' with the following content:
Aabcdefgh
Babcdefgh
cabcdefgh
eabcd
Now you can use the following command to grep in this file:
grep -Eo '^[A-Z][[:alpha:]]{8}' testfile
The code above is equal to:
cat testfile | grep -Eo '^[A-Z][[:alpha:]]{8}'
This matches
Aabcdefgh
Babcdefgh
I need to search for a PHP variable $someVar. However, Grep thinks that I am trying to run a regex and is complaining:
$ grep -ir "Something Here" * | grep $someVar
Usage: grep [OPTION]... PATTERN [FILE]...
Try `grep --help' for more information.
$ grep -ir "Something Here" * | grep "$someVar"
<<Here it returns all rows with "someVar", not only those with "$someVar">>
I don't see an option for telling grep not to interpret the string as a regex, but to include the $ as just another string character.
Use fgrep (deprecated), grep -F or grep --fixed-strings, to make it treat the pattern as a list of fixed strings, instead of a regex.
For reference, the documentation mentions (excerpts):
-F --fixed-strings Interpret the pattern as a list of fixed
strings (instead of regular expressions), separated by newlines, any
of which is to be matched. (-F is specified by POSIX.)
fgrep is the same as grep -F. Direct invocation as fgrep is
deprecated, but is provided to allow historical applications that rely
on them to run unmodified.
For the complete reference, check:
https://www.gnu.org/savannah-checkouts/gnu/grep/manual/grep.html
grep -F is a standard way to tell grep to interpret argument as a fixed string, not a pattern.
You have to tell grep you use a fixed-string, instead of a pattern, using '-F' :
grep -ir "Something Here" * | grep -F \$somevar
In this question, the main issue is not about grep interpreting $ as a regex. It's about the shell substituting $someVar with the value of the environment variable someVar, likely the empty string.
So in the first example, it's like calling grep without any argument, and that's why it gives you a usage output. The second example should not return all rows containing someVar but all lines, because the empty string is in all lines.
To tell the shell to not substitute, you have to use '$someVar' or \$someVar. Then you'll have to deal with the grep interpretation of the $ character, hence the grep -F option given in many other answers.
So one valid answer would be:
grep -ir "Something Here" * | grep '$someVar'
+1 for the -F option, it shall be the accepted answer.
Also, I had a "strange" behaviour while searching for the -I.. pattern in my files, as the -I was considered as an option of grep ; to avoid such kind of errors, we can explicitly specify the end of the arguments of the command using --.
Example:
grep -HnrF -- <pattern> <files>
Hope that'll help someone.
Escape the $ by putting a \ in front of it.