LinuxQuestions.org
Help answer threads with 0 replies.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 05-18-2019, 10:34 PM   #1
Turbocapitalist
LQ Guru
 
Registered: Apr 2005
Distribution: Linux Mint, Devuan, OpenBSD
Posts: 7,307
Blog Entries: 3

Rep: Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721
Collapsing a Bash array into a comma-separated string


I'm looking to find Bash-only way to efficiently concatenate all positional parameters passed to a shell function. They should all end up in a single string but separated by commas. What I came up with first was this:

Code:
tb ()
{
    local files=$(realpath $@);
    local att=$(echo $files | tr ' ' ',');
    echo foo "a='$att'"
}
So if I write

Code:
pwd
tb a
tb b c d
I would get

Code:
/home/me
foo a='/home/me/a'
foo a='/home/me/b,/home/me/c,/home/me/d'
However, that will choke on file names and paths containing spaces. I see several other ways, but they all seem to leave a trailing comma and that would break things. Or they require calling sed or other programs.

How should I really merge $1, $2, $3, ..., $n into a single, comma-separated string using just bash?
 
Old 05-19-2019, 01:31 AM   #2
ondoho
LQ Addict
 
Registered: Dec 2013
Posts: 19,872
Blog Entries: 12

Rep: Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053
I will take your thread title: Collapsing a Bash array into a comma-separated string literally:
Code:
array=( ... ) # assuming this is filled already
string=""
for i in "${array[@]}" # this exact syntax will preserve array elements even if they have spaces in them
do
    string="$i,$string"
done
# we have a superfluous trailing comma now, remove it:
string="${string%,}"
(not tested)
 
Old 05-19-2019, 02:13 AM   #3
astrogeek
Moderator
 
Registered: Oct 2008
Distribution: Slackware [64]-X.{0|1|2|37|-current} ::12<=X<=15, FreeBSD_12{.0|.1}
Posts: 6,263
Blog Entries: 24

Rep: Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194
Here is a simple loop that should do it, assuming arr is your already set array:

Code:
for i in "${arr[@]}"; do 
    str="${str}${c}${i}" 
    c=',' 
done
 
Old 05-19-2019, 02:21 AM   #4
ondoho
LQ Addict
 
Registered: Dec 2013
Posts: 19,872
Blog Entries: 12

Rep: Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053Reputation: 6053
^ code golf!
works for a standalone bash script; uninitialised variables default to the empty string.
 
Old 05-19-2019, 03:03 AM   #5
Turbocapitalist
LQ Guru
 
Registered: Apr 2005
Distribution: Linux Mint, Devuan, OpenBSD
Posts: 7,307

Original Poster
Blog Entries: 3

Rep: Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721
Ok. Thanks, both.

I'll go with this one for the time being:

Code:
tb() {  local c i str;
        for i in "${@}"; do
                i=$(realpath $i);
                str="${str}${c}${i}"
                c=','
        done;
        echo str=$str; }
It seems there are problems reading from realpath even with the --zero option. As an example, the following also chokes on file names and paths with spaces:

Code:
tx() {  local c i str;
        while read -r -d '' i; do
                str="${str}${c}${i}"
                c=','
        done < <(realpath --zero ${@});
        echo str=$str; }
 
Old 05-19-2019, 03:19 AM   #6
astrogeek
Moderator
 
Registered: Oct 2008
Distribution: Slackware [64]-X.{0|1|2|37|-current} ::12<=X<=15, FreeBSD_12{.0|.1}
Posts: 6,263
Blog Entries: 24

Rep: Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194
Quote:
Originally Posted by Turbocapitalist View Post
It seems there are problems reading from realpath even with the --zero option. As an example, the following also chokes on file names and paths with spaces:
Try quoting the argument to realpath...

Code:
tb() {  local c i str;
        for i in "${@}"; do
                i=$(realpath "$i");
                str="${str}${c}${i}"
                c=','
        done;
        echo str=$str; }
Or...


Code:
tx() {  local c i str;
        while read -r -d '' i; do
                str="${str}${c}${i}"
                c=','
        done < <(realpath --zero "${@}");
        echo str=$str; }

Last edited by astrogeek; 05-19-2019 at 03:23 AM. Reason: Remove orphan QUOTE
 
1 members found this post helpful.
Old 05-19-2019, 03:22 AM   #7
Turbocapitalist
LQ Guru
 
Registered: Apr 2005
Distribution: Linux Mint, Devuan, OpenBSD
Posts: 7,307

Original Poster
Blog Entries: 3

Rep: Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721
Quote:
Originally Posted by astrogeek View Post
Try quoting the argument to realpath...
Thanks. I had tried just about everything except that. It was the original input that was off. GIGO.
 
Old 05-19-2019, 03:26 AM   #8
astrogeek
Moderator
 
Registered: Oct 2008
Distribution: Slackware [64]-X.{0|1|2|37|-current} ::12<=X<=15, FreeBSD_12{.0|.1}
Posts: 6,263
Blog Entries: 24

Rep: Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194
We all do it! You are welcome!
 
Old 05-19-2019, 05:17 AM   #9
syg00
LQ Veteran
 
Registered: Aug 2003
Location: Australia
Distribution: Lots ...
Posts: 21,126

Rep: Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120
I always found bash "interesting" with quotes - a PITA actually. Not worth the grief - if another tool does it easier/better I'll use that.
But I do admire the persistence of others, and the solutions you all come up with.
 
Old 05-19-2019, 05:27 AM   #10
pan64
LQ Addict
 
Registered: Mar 2012
Location: Hungary
Distribution: debian/ubuntu/suse ...
Posts: 21,838

Rep: Reputation: 7308Reputation: 7308Reputation: 7308Reputation: 7308Reputation: 7308Reputation: 7308Reputation: 7308Reputation: 7308Reputation: 7308Reputation: 7308Reputation: 7308
I would suggest you to use shellcheck, it will definitely help you to solve this issue.
 
Old 05-19-2019, 05:32 AM   #11
Turbocapitalist
LQ Guru
 
Registered: Apr 2005
Distribution: Linux Mint, Devuan, OpenBSD
Posts: 7,307

Original Poster
Blog Entries: 3

Rep: Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721Reputation: 3721
Quote:
Originally Posted by syg00 View Post
I always found bash "interesting" with quotes - a PITA actually. Not worth the grief - if another tool does it easier/better I'll use that.
But I do admire the persistence of others, and the solutions you all come up with.
I'm not keen on bash much either but it is the default shell for many distros and a few other operating systems.

The actual problem I am trying to solve is a work-around for Thunderbird's inability to accept relative paths for attachments submitted via the shell. So I have made a shell function to first calculate the full absolute path of each attachment and then submit all that to Thunderbird in such a way that they are attached to a new message.

I'm open for suggestions on other ways to solve that.

As for shells, I've tended to stay with defaults but have been experimented more and more with zsh on my GNU/Linux systems and am contemplating eventual migration.
 
Old 05-19-2019, 06:01 AM   #12
grail
LQ Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 10,007

Rep: Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191
I know this one has been solved, but I cam to the party late, so how about:
Code:
#!/usr/bin/env bash

tb()
{
	local -a files

	mapfile -t files <<<"$(realpath "$@")"

	IFS=","
	echo "foo a='${files[*]}'"
}

tb "$@"
 
2 members found this post helpful.
Old 05-19-2019, 06:04 AM   #13
syg00
LQ Veteran
 
Registered: Aug 2003
Location: Australia
Distribution: Lots ...
Posts: 21,126

Rep: Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120
C'mon grail - "pure bash".
coreutils is GNU, but it ain't bash.
 
Old 05-19-2019, 08:17 AM   #14
grail
LQ Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 10,007

Rep: Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191Reputation: 3191
Quote:
Originally Posted by syg00 View Post
C'mon grail - "pure bash".
coreutils is GNU, but it ain't bash.
Hey, I didn't bring in realpath ... that was from OP ... so the rest is "pure bash" .... otherwise adding any path would be a non-issue
 
Old 05-20-2019, 02:29 AM   #15
NevemTeve
Senior Member
 
Registered: Oct 2011
Location: Budapest
Distribution: Debian/GNU/Linux, AIX
Posts: 4,862
Blog Entries: 1

Rep: Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869
Using printf:
Code:
array=(1 "2 2" '3 3')
Tmp=$(printf "%s," "${array[@]}")
echo ${Tmp%,}

Last edited by NevemTeve; 05-20-2019 at 11:51 AM.
 
1 members found this post helpful.
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
[SOLVED] Store array output to comma separated list in bash scripting Rock26 Linux - Newbie 1 07-27-2016 10:26 PM
[SOLVED] Assigning Multiple Comma Separated IP's To A Bash Array metallica1973 Programming 3 06-29-2012 04:30 AM
[SOLVED] Bash script to merge files together (given as a comma separated string) DomeKor Linux - Newbie 10 09-27-2011 11:29 PM
[SOLVED] Randomize the comma separated string in shell mariakumar Linux - General 13 10-11-2010 12:32 AM
How to delete Comma in a comma separated file with double quotes as quote character pklcnu Linux - Newbie 2 03-24-2009 05:50 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 09:14 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration