3

Sample Data: Tab separated file (TSV)

a.1.58  fadado/CSV  https://github.com/fadado/CSV
a.1.63  jehiah/json2csv https://github.com/jehiah/json2csv
a.1.80  stedolan/jq https://github.com/stedolan/jq/issues/370

Following selects one record using fzf, and stores 2nd and 3rd column to an array Link:

mapfile -d $'\t' -t Link < <(awk 'BEGIN{FS="\t"; OFS="\t"} {print $2,$3}' "${SessionP}" | fzf)

Issue

In the above command I have used -t option of mapfile, but echo "${Link[1]}" prints a trailing new line!

Why is it not getting eliminated?

Reference

1
  • 4
    Perhaps IFS=$'\t' read -ra Link < <( ... ) might suit your purpose better Commented Jul 26, 2021 at 10:54

3 Answers 3

12

Check your local documentation instead of the documentation found someplace else on the web. In an interactive bash shell session, type help mapfile, or look up he documentation for mapfile in the bash manual (man bash). Depending on your version of bash, the documentation may vary from what's found on the web.

On my system, with bash 5.1.8, help mapfile documents the -t option to mapfile like this:

-t Remove a trailing DELIM from each line read (default newline)

The DELIM is set with -d:

-d delim Use DELIM to terminate lines, instead of newline

This means that when using -d $'\t' -t with mapfile, it would remove a trailing tab character, if there was one, not a trailing newline character.

The bash shell has had mapfile -d since release 4.4. The introduction of this option was documented like this:

The mapfile builtin now has a -d option to use an arbitrary character as the record delimiter, and a -t option to strip the delimiter as supplied with -d.

To remove the trailing newline from your data when printing the last element, use "${Link[1]%$'\n'}" when outputting the element. This removes the last newline from the element if the last character is a newline character.

2

If the input data (after awk and fzf) is something like

fadado/CSV<tab>https://github.com/fadado/CSV<nl>

and you want those two fields separately, then mapfile isn't necessarily even the best tool. Instead, you could use read -a to read to an array, or read with two variable names:

This would read the two fields to ${arr[0]} and ${arr[1]}

IFS=$'\t' read -r -a arr < ...

or

IFS=$'\t' read -r x y < ...

E.g. (skipping the process substitution for the sake of the example):

IFS=$'\t' read -r -a arr <<< $'fadado/CSV\thttps://github.com/fadado/CSV\n'
printf "'%s' '%s'\n" "${arr[0]}" "${arr[1]}"

prints

'fadado/CSV' 'https://github.com/fadado/CSV'
0

The previous answers are confusing the issue. The problem is not "mapfile fails to remove a trailing newline", because there is no trailing newline to remove. It's true that the -t option here means to remove trailing delimiters (here, the tabs), not trailing newlines. And it does remove the trailing tabs. But there were no newlines to begin with, so mapfile is not failing to remove anything.

(As far as I can tell, null delimiters are a special case; they'll effectively be removed even without -t.)

The problem is, instead: "echo adds a trailing newline". mapfile has already run at this point, and can't do anything about that.

(Without -t, there would be a trailing tab followed by newline on each echoed entry; the tab may be hard to observe in the terminal - although it's easier if you suppress the newline.)

We can see the problem by directly creating an array in Bash instead:

$ a=(1 2 3)
$ for num in "${a[@]}"; do echo "$num"; done
1
2
3

The output numbers appear on separate lines, not because echo is used multiple times, but because each run of echo actually has to output the newline.

To avoid this, we can for example use:

$ for num in "${a[@]}"; do printf '%s' "$num"; done
123$

Notice that the prompt appears on the same line as the output.

The "${Link[1]%$'\n'}" trick will not work for the situation described, because again, the newline is not in the data nor created by the shell interpolation, but emitted separately by echo:

$ echo "${a[0]%$'\n'}"; echo "${a[1]%$'\n'}"
1
2
$ printf '%s' "${a[0]}"; printf '%s' "${a[1]}"
12$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.