69
COMPANY_NAME=`cat file.txt | grep "company_name" | cut -d '=' -f 2` 

outputs something like this

"Abc Inc";

What I want to do is I want to remove the trailing ";" as well. How can i do that? I am a beginner to bash. Any thoughts or suggestions would be helpful.

Laurence Gonsalves
  • 137,896
  • 35
  • 246
  • 299
liv2hak
  • 14,472
  • 53
  • 157
  • 270
  • 1
    Same here. `cat`/`grep` = `grep`. – Det May 17 '13 at 22:10
  • 1
    For the problem at hand, it could have been solve with just grep: COMPANY_NAME=$(grep -Po '(?<=company_name=)"[^"]*"' file.txt) – Bo R Mar 05 '18 at 13:16

14 Answers14

78

This will remove the last character contained in your COMPANY_NAME var regardless if it is or not a semicolon:

echo "$COMPANY_NAME" | rev | cut -c 2- | rev
ztank1013
  • 6,939
  • 2
  • 22
  • 20
59
foo="hello world"
echo ${foo%?}
hello worl
grokster
  • 5,919
  • 1
  • 36
  • 22
  • 7
    THIS is the bash way, not the other piped contraptions. – MLu Sep 08 '15 at 04:36
  • 3
    I like it. Add extra ? for more characters to be removed. – 22332112 Oct 28 '16 at 18:42
  • 2
    This just made my day because I was trying to make a quick and dirty list of websites we had TLS keys for. `for L in \`ls *key\` ; do echo ${L%.key} ; done` – Rache Mar 26 '18 at 16:52
59

I'd use sed 's/;$//'. eg:

COMPANY_NAME=`cat file.txt | grep "company_name" | cut -d '=' -f 2 | sed 's/;$//'`
Laurence Gonsalves
  • 137,896
  • 35
  • 246
  • 299
  • 12
    Don't abuse `cat`s. `grep` can actually read files, did you know? ;-) – Has QUIT--Anony-Mousse Feb 04 '13 at 20:44
  • 11
    @Anony-Mousse Yes, I know there are at least two ways to avoid `cat` here. I left in the cat in order to avoid changing the command line from the question beyond what was actually necessary to make it work. – Laurence Gonsalves Feb 04 '13 at 20:49
  • 3
    @Anony-Mousse Not really in all cases, simply grep without cat -v will hide invisible (e.g. malicious) characters, http://unix.stackexchange.com/questions/202198/bash-why-does-x0d-x20-erase-the-line – 林果皞 Jan 08 '16 at 12:10
34

I'd use head --bytes -1, or head -c-1 for short.

COMPANY_NAME=`cat file.txt | grep "company_name" | cut -d '=' -f 2 | head --bytes -1`

head outputs only the beginning of a stream or file. Typically it counts lines, but it can be made to count characters/bytes instead. head --bytes 10 will output the first ten characters, but head --bytes -10 will output everything except the last ten.

NB: you may have issues if the final character is multi-byte, but a semi-colon isn't

I'd recommend this solution over sed or cut because

  • It's exactly what head was designed to do, thus less command-line options and an easier-to-read command
  • It saves you having to think about regular expressions, which are cool/powerful but often overkill
  • It saves your machine having to think about regular expressions, so will be imperceptibly faster
Aaron J Lang
  • 2,048
  • 3
  • 20
  • 27
15

I believe the cleanest way to strip a single character from a string with bash is:

echo ${COMPANY_NAME:: -1}

but I haven't been able to embed the grep piece within the curly braces, so your particular task becomes a two-liner:

COMPANY_NAME=$(grep "company_name" file.txt); COMPANY_NAME=${COMPANY_NAME:: -1} 

This will strip any character, semicolon or not, but can get rid of the semicolon specifically, too. To remove ALL semicolons, wherever they may fall:

echo ${COMPANY_NAME/;/}

To remove only a semicolon at the end:

echo ${COMPANY_NAME%;}

Or, to remove multiple semicolons from the end:

echo ${COMPANY_NAME%%;}

For great detail and more on this approach, The Linux Documentation Project covers a lot of ground at http://tldp.org/LDP/abs/html/string-manipulation.html

mightypile
  • 7,589
  • 3
  • 37
  • 42
8

Using sed, if you don't know what the last character actually is:

$ grep company_name file.txt | cut -d '=' -f2 | sed 's/.$//'
"Abc Inc"
Det
  • 3,640
  • 5
  • 20
  • 27
5

Don't abuse cats. Did you know that grep can read files, too?

The canonical approach would be this:

grep "company_name" file.txt | cut -d '=' -f 2 | sed -e 's/;$//'

the smarter approach would use a single perl or awk statement, which can do filter and different transformations at once. For example something like this:

COMPANY_NAME=$( perl -ne '/company_name=(.*);/ && print $1' file.txt )
Has QUIT--Anony-Mousse
  • 76,138
  • 12
  • 138
  • 194
3

don't have to chain so many tools. Just one awk command does the job

 COMPANY_NAME=$(awk -F"=" '/company_name/{gsub(/;$/,"",$2) ;print $2}' file.txt)
kurumi
  • 25,121
  • 5
  • 44
  • 52
2

you can strip the beginnings and ends of a string by N characters using this bash construct, as someone said already

$ fred=abcdefg.rpm
$ echo ${fred:1:-4}
bcdefg

HOWEVER, this is not supported in older versions of bash.. as I discovered just now writing a script for a Red hat EL6 install process. This is the sole reason for posting here. A hacky way to achieve this is to use sed with extended regex like this:

$ fred=abcdefg.rpm
$ echo $fred | sed -re 's/^.(.*)....$/\1/g'
bcdefg
Paul M
  • 290
  • 2
  • 12
1

Assuming the quotation marks are actually part of the output, couldn't you just use the -o switch to return everything between the quote marks?

COMPANY_NAME="\"ABC Inc\";" | echo $COMPANY_NAME | grep -o "\"*.*\""
Jens Bodal
  • 1,707
  • 1
  • 22
  • 32
1

Some refinements to answer above. To remove more than one char you add multiple question marks. For example, to remove last two chars from variable $SRC_IP_MSG, you can use:

SRC_IP_MSG=${SRC_IP_MSG%??}
yuliskov
  • 1,379
  • 15
  • 16
1

In Bash using only one external utility:

IFS='= ' read -r discard COMPANY_NAME <<< $(grep "company_name" file.txt)
COMPANY_NAME=${COMPANY_NAME/%?}
Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
0

I am not finding that sed 's/;$//' works. It doesn't trim anything, though I'm wondering whether it's because the character I'm trying to trim off happens to be a "$". What does work for me is sed 's/.\{1\}$//'.

ktappe
  • 109
  • 5
0
cat file.txt | grep "company_name" | cut -d '=' -f 2 | cut -d ';' -f 1
j0k
  • 22,600
  • 28
  • 79
  • 90
necromancer
  • 23,916
  • 22
  • 68
  • 115
  • that was obvious!.sorry I overlooked that obvious thing. – liv2hak Feb 22 '11 at 06:48
  • as linus torvalds says, one of the most difficult things to find in a programmer is "good taste", which is difficult to define. but since you would benefit from it i will do so in this case: because of the initial `cat` every element of the pipeline that does real work operates on its standard input and its standard output. the benefit is that you can replace the `cat` with some other pipeline that produces output similar to `file.txt` and you don't have to change even a single character in the functional part of the pipeline. this allows drop-in pipeline reusability. – necromancer May 17 '13 at 22:26
  • @Det fyb above (forgot to tag you, just like somebody might forget to remove the input argument when composing pipelines and wonder why the output isn't as expected) – necromancer May 17 '13 at 22:29
  • @randomstring, while I don't really see this as you giving me a ["piece of your mind"](http://stackoverflow.com/a/16619430/1821548) (I lol'd by the way) it's rather extreme reusability to preserve the use of `cat` just to give you the possibility of changing it to something else later on. The only thing I could see it worth for is simplicity. If a user types `grep pattern file`, then I'm pretty damn sure he understands it's that first part which reads the file and where he starts piping it. – Det May 18 '13 at 03:46
  • @Det thanks for taking it positively. it is reasonable to say `grep foo file | grep bar | wc` and it is reasonable to say `cat file | grep foo | grep bar | wc`. what is not reasonable is to give out useless cat awards for the second. there are several specific points to preseve them in my thesis on the subject. perhaps you might not appreciate them now as i didn't when i was not as experienced. i got pissed off not at you but at the people who should know better than to initiate and support the practice. hope that makes sense. – necromancer May 18 '13 at 06:38
  • @Det my answer's now the accepted one; of course that's only one person's opinion other than me. thanks for the motivation to write it out. – necromancer May 18 '13 at 20:10
  • @randomstring well, it's much better than the one OP originally accepted. The discussion there is also very useful and clarifies the point I was trying to make quite nicely. I upped your answer too. Also, it's not like _I_ was the one who had the motivation to go write an answer we can all be proud of. So all in all: glad you got pissed off. Even if not at me. – Det May 19 '13 at 05:33
  • @Det why thank you! :-) looks like you are learning unix stuff with an eye on thoroughness. that's the right approach so wish you good luck! ps: i should offer my apologies in advance in case i am wrong and you are a unix veteran just looking to fill a couple of gaps in knowledge. LOL! – necromancer May 19 '13 at 06:23
  • 1
    @randomstring far from it. – Det Jun 12 '13 at 14:28