HarryH
HarryH

Reputation: 165

How do I correctly retrieve, using bash' cut, the first field from a line with only 1 field in a text file?

In a text file (accounts.txt) with (financial) accounts the sub-accounts are, and need to be, separated by an underscore, looking like this:

assets
assets_hh
assets_hh_reimbursements
assets_hh_reimbursements_ff

... etc.

Now I want to get specific sub-accounts from specific line numbers, e.g.: field 3 from line 4:

$ lnr=4; fnr=3
$ cut -d $'\n' -f "$lnr" < accounts.txt | cut -d _ -f "$fnr"
reimbursements
$

But both fnr=1 and fnr=2 give for the first line, which has only 1 field:

$ cut -d $'\n' -f 1 < accounts.txt | cut -d _ -f "fnr"
assets
$

which is undesired behaviour.

Now I can get around this by prefixing an underscore to each account and add 1 to each required field number, but this is not an elegant solution. Am I doing something wrong and/or can this be changed by issuing a different retrieval command?

Upvotes: 0

Views: 96

Answers (3)

Olli K
Olli K

Reputation: 1760

One solution is to head|tail and read into an array so it's easier to work with the items:

lnr=4
fnr=2
IFS=_ read -r -a arr < <(head -n "$lnr" accounts.txt | tail -n 1)
#note that the array is 0-indexed, so the fieldnumber has to fit that
echo "${arr[$fnr]}"

Then you could expand the idea into a more usable function:

get_field_from_file() {
    local fname="$1"
    local lnr="$2"
    local fnr="$3"
    IFS=_ read -r -a arr < <(head -n "$lnr" "$fname" | tail -n 1)
    if (( $fnr > ${#arr[@]} )); then
        return 1
    else
        echo "${arr[$fnr]}"
    fi
}

field=$(get_field_from_file "accounts.txt" "4" "2") || echo "no such line or field"
[[ -n $field ]] && echo "field: $field"

Upvotes: 0

VIPIN KUMAR
VIPIN KUMAR

Reputation: 3147

Check below solution -

cat f
assets
assets_hh
assets_hh_reimbursements
assets_hh_reimbursements_ff

Based on your comment try below commands -

$ lnr=1; fnr=2
$ echo $lnr $fnr
1 2
$ awk -v lnr=$lnr -v fnr=$fnr  -F'_' 'NR==lnr  {print $fnr}' f
             ###Output is nothing as line 1 column 2 is blank when FS="_"
$ lnr=4;fnr=1
$ echo $lnr $fnr
4 1
$ awk -v lnr=$lnr -v fnr=$fnr  -F'_' 'NR==lnr  {print $fnr}' f
assets
$ lnr=4;fnr=3
$ echo $lnr $fnr
4 3
$ awk -v lnr=$lnr -v fnr=$fnr  -F'_' 'NR==lnr  {print $fnr}' f
reimbursements

Upvotes: 1

clt60
clt60

Reputation: 63974

Using the cut -d $'\n' -f "$lnr" for getting the lnr-th line from the file is somewhat strange. More common approach is using sed, like:

sed -n "${lnr}p" file | cmd ...

However, for this the awk is better - in one invocation could handle the lnr and fnr too.

file=accounts.txt
lnr=1
fnr=2
awk -F_ -v l=$lnr -v f=$fnr 'NR==l{print $f}' "$file"

The above for the all combinations lnr/fnr produces:

line                          field1   field2   field3           field4
------------------------------------------------------------------------
assets                        assets
assets_hh                     assets   hh
assets_hh_reimbursements      assets   hh       reimbursements
assets_hh_reimbursements_ff   assets   hh       reimbursements   ff

Upvotes: 2

Related Questions