Chet
Chet

Reputation: 19879

How to split, map, and join in Bash?

I want to create a simple regular expression to match some files. The command npm ls --dev --parseable prints out a bunch of files, for example:

/Users/chetcorcos/code/dev-tool/node_modules/fsevents/node_modules/tough-cookie
/Users/chetcorcos/code/dev-tool/node_modules/fsevents/node_modules/tunnel-agent
/Users/chetcorcos/code/dev-tool/node_modules/fsevents/node_modules/rimraf
/Users/chetcorcos/code/dev-tool/node_modules/fsevents/node_modules/rimraf/node_modules/glob
/Users/chetcorcos/code/dev-tool/node_modules/fsevents/node_modules/rimraf/node_modules/glob/node_modules/inflight
/Users/chetcorcos/code/dev-tool/node_modules/fsevents/node_modules/rimraf/node_modules/glob/node_modules/inflight/node_modules/wrappy

I want to get back a string that looks something like this:

tough-cookie|tunnel-agent|rimraf|inflight|wrappy

To get this, I want to "split by newline, map over basename, and join with a pipe". In JavaScript with Ramdajs, I'd so something like this:

R.pipe(R.split('\n'), R.map(R.split('/')), R.map(R.nth(-1)), R.join('|'))

Any ideas how to do something like this in bash? Whats the idiomatic way of doing this?

Upvotes: 3

Views: 1753

Answers (3)

mcgru
mcgru

Reputation: 11

You already have a 'list' of strings, separated by '\n'. Just map basename on each item (using xargs) - then you'll get list of basenames separated by '\n' plus final '\n'. Then replace each '\n' with '|' symbol:

anycmd | xargs -r -n1 basename | tr '\n' '|'

You may then remove last '|' by either sed or second xargs.

anycmd | xargs -r -n1 basename | tr '\n' '|' | sed 's/|$//'

or

anycmd | xargs -r -n1 basename | xargs | tr ' ' '|'

Upvotes: 1

Charles Duffy
Charles Duffy

Reputation: 295629

Bash doesn't have functional programming primitives built in. It's possible to build them with a hundred lines of code or so, but also not particularly worth it for this kind of use case.


Consider:

content=$(npm ls --dev --parseable | sed -e 's@.*/@@' | paste -s -d '|')
echo "$content"

...this routes the stdout of NPM into sed, telling it to replace everything up to the last slash in each line with an empty string, and then routing the stdout of sed into paste, using that to combine all lines into a single string with | separating them.


Alternately, to use no tools not built into bash itself (other than your data source, npm):

#!/bin/bash
# note that this requires bash 4.0 or later
mapfile -t lines < <(npm ls --dev --parseable) # read content into array
lines=( "${lines[@]##*/}" )                    # trim everything prior to last / in each
(IFS='|'; printf '%s\n' "${lines[*]}")         # emit array as a single string with |s

Upvotes: 4

JNevill
JNevill

Reputation: 50200

You could just pipe that thing to awk and have awk pick off the last element:

 npm ls --dev --parseable | awk -F"/" '{output=output$(NF)"|"} END { sub(/[|]+$/, "", output); print output }' 

That awk script will split incoming records by /, capture the last element $(NF) to variable output with a pipe to delimit, Then once complete, will strip the last pipe using gsub and spit the results out

Upvotes: 1

Related Questions