43

I'm trying to split a tab delimitted field in bash.

I am aware of this answer: how to split a string in shell and get the last field

But that does not answer for a tab character.

I want to do get the part of a string before the tab character, so I'm doing this:

x=`head -1 my-file.txt`
echo ${x%\t*}

But the \t is matching on the letter 't' and not on a tab. What is the best way to do this?

Thanks

Community
  • 1
  • 1
chaimp
  • 16,897
  • 16
  • 53
  • 86
  • If you're making a handy one-liner right on the command line, you can press Ctrl+V, then TAB to insert a TAB char. – user208145 Oct 14 '18 at 03:31

7 Answers7

76

If your file look something like this (with tab as separator):

1st-field   2nd-field

you can use cut to extract the first field (operates on tab by default):

$ cut -f1 input
1st-field

If you're using awk, there is no need to use tail to get the last line, changing the input to:

1:1st-field     2nd-field
2:1st-field     2nd-field
3:1st-field     2nd-field
4:1st-field     2nd-field
5:1st-field     2nd-field
6:1st-field     2nd-field
7:1st-field     2nd-field
8:1st-field     2nd-field
9:1st-field     2nd-field
10:1st-field    2nd-field

Solution using awk:

$ awk 'END {print $1}' input
10:1st-field

Pure bash-solution:

#!/bin/bash

while read a b;do last=$a; done < input
echo $last

outputs:

$ ./tab.sh 
10:1st-field

Lastly, a solution using sed

$ sed '$s/\(^[^\t]*\).*$/\1/' input
10:1st-field

here, $ is the range operator; i.e. operate on the last line only.

For your original question, use a literal tab, i.e.

x="1st-field    2nd-field"
echo ${x%   *}

outputs:

1st-field
Fredrik Pihl
  • 44,604
  • 7
  • 83
  • 130
19

Use $'ANSI-C' strings in the parameter expansion:

$ x=$'abc\tdef\tghi'
$ echo "$s"
abc     def     ghi
$ echo ">>${x%%$'\t'*}<<"
>>abc<<
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • 1
    To me, this solution, using `$'\t'` within the `{}` parameter expansion, fits what the OP was asking. I used this to speed up a script of mine by 76% over using multiple pipes to `cut`. – user208145 Oct 14 '18 at 03:34
9
read field1 field2 <<< ${tabDelimitedField}

or

read field1 field2 <<< $(command_producing_tab_delimited_output)
gniourf_gniourf
  • 44,650
  • 9
  • 93
  • 104
user9118869
  • 99
  • 1
  • 2
  • Please augment your code-only answer with some explanation, in order to reduce the impression that StackOverflow is a free code writing service. – Yunnosch Dec 19 '17 at 18:56
  • 1
    While being elegant, this seems to split by any whitespace, space or tabs. To split by tab only, use `IFS=$'\t'` before the command. – robrecord Dec 14 '22 at 16:20
8

Use awk.

echo $yourfield | awk '{print $1}'

or, in your case, for the first field from the the last line of a file

tail yourfile | awk '{x=$1}END{print x}'
Michael
  • 4,010
  • 4
  • 28
  • 49
  • 1
    Thanks - that was it, with one correction: awk -F"\t" '{x=$1}END{print x}' – chaimp Jul 11 '11 at 18:55
  • Default awk field seperator is whitespace, which includes tab - but maybe your application needed to narrow it. – Michael Jul 11 '11 at 19:06
4

There is an easy way for a tab separated string : convert it to an array.

Create a string with tabs ($ added before for '\t' interpretation) :

AAA=$'ABC\tDEF\tGHI'

Split the string as an array using parenthesis :

BBB=($AAA) 

Get access to any element :

echo ${BBB[0]}
ABC
echo ${BBB[1]}
DEF
echo ${BBB[2]}
GHI
2

x=first$'\t'second
echo "${x%$'\t'*}"

See QUOTING in man bash

Luchostein
  • 2,314
  • 20
  • 24
  • 2
    And to only get "second" use: `echo "${x#*$'\t'}"`. See Parameter Expansion in `man bash` – erwin Sep 07 '22 at 04:03
0

The answer from https://stackoverflow.com/users/1815797/gniourf-gniourf hints at the use of built in field parsing in bash, but does not really complete the answer. The use of the IFS shell parameter to set the input field separate will complete the picture and give the ability to parse files which are tab-delimited, of a fixed number of fields, in pure bash.

echo -e "a\tb\tc\nd\te\tf" > myfile
while IFS='<literaltab>' read f1 f2 f3;do echo "$f1 = $f2 + $f3"; done < myfile

a = b + c
d = e + f

Where, of course, is replaced by a real tab, not \t. Often, Control-V Tab does this in a terminal.

Kevin Buchs
  • 2,520
  • 4
  • 36
  • 55