3

A simple Example: mybin *.txt will expand to mybin a.txt b.txt c.txt

But I'm looking for a simple solution to have an expansion to something like: mybin --conf a.txt --conf b.txt --conf c.txt.

Is there a built in to do this? What is the simplest way for this?

powerpete
  • 2,663
  • 2
  • 23
  • 49
  • I don't know if there is an easy way to do so, but if you need more than a conf I would follow the lead of the OS... For example, `apt` has its `apt.conf.d` folder, as many other programs – ingroxd Sep 27 '18 at 06:34
  • `sed 's/\(^.*$\)/--conf \1/g' < <(ls -1 *.txt)` (where that is `ls -one`...) You can simply remove the new lines with a `printf "%s "`, e.g. `printf "%s " $(sed 's/\(^.*$\)/--conf \1/g' < <(ls -1 *.txt))` – David C. Rankin Sep 27 '18 at 07:40
  • Is `mybin` capable of reading configs from `stdin` ? – oguz ismail Sep 27 '18 at 08:11
  • If so, I think you can do sth like this `cat *.txt | mybin --conf -`, which is a more secure way to do this than using `eval` – oguz ismail Sep 27 '18 at 08:24

5 Answers5

1

find is my friend:

mybin $(find /wherever/ -name '*.txt' -printf '--conf %p ')
powerpete
  • 2,663
  • 2
  • 23
  • 49
  • if filenames contain spaces or special characters ... `eval mybin "$(find ./ -name '*.txt' -printf '--conf "%p" ')"` – Feng Sep 27 '18 at 07:33
  • Dangerous: If a file is named `$( rm -rf / )`, that code will be executed. Also, with Feng's modification, `"` in filenames will fail (analogously if you used `'`). `printf %q` is how you should do it. – philipp2100 Apr 08 '22 at 09:35
0

A bit tricky solution:

eval mybin "--conf\ {`echo *.txt|tr -s " " ,`}"
Ipor Sircer
  • 3,069
  • 3
  • 10
  • 15
0

for all txt files

eval mybin "$(printf -- '--conf %q ' *.txt)"

If only for certain txt files

eval mybin '--conf "'{a,b,c}.txt'"'

Maybe we should use a wrapper function. This is not a builtin solution, but it works well than the previous two commands if filenames contain spaces or special characters.

function mybinw:

function mybinw() {
  declare -a mybin_opts

  for file in "$@"; do
    mybin_opts+=(--conf "$file")
  done

  mybin "${mybin_opts[@]}"
}

Test:

mybin:

#!/bin/bash

for q in "$@"; do
  echo "=> $q"
done

Create some txt files, some file names include spaces or special characters

touch {a,b,c,d,efg,"h h"}.txt 'a(1).txt' 'b;b.txt'

For all txt files:

eval mybin "$(printf -- '--conf %q ' *.txt)"
=> --conf
=> a(1).txt
=> --conf
=> a.txt
=> --conf
=> b;b.txt
=> --conf
=> b.txt
=> --conf
=> c.txt
=> --conf
=> d.txt
=> --conf
=> efg.txt
=> --conf
=> h h.txt

for certain txt files:

eval mybin '--conf "'{a,b,c,"h h"}.txt'"'
=> --conf
=> a.txt
=> --conf
=> b.txt
=> --conf
=> c.txt
=> --conf
=> h h.txt

Using a wrapper function

touch 'c"c.txt'

mybinw *.txt
=> --conf
=> a(1).txt
=> --conf
=> a"b.txt
=> --conf
=> a.txt
=> --conf
=> b;b.txt
=> --conf
=> b.txt
=> --conf
=> c"c.txt
=> --conf
=> c.txt
=> --conf
=> d.txt
=> --conf
=> efg.txt
=> --conf
=> h h.txt
Feng
  • 3,592
  • 2
  • 15
  • 14
0
# usage mix command switch args ...
mix(){
        p=$1; shift; q=$1; shift; c=
        i=1; for a; do c="$c $q \"\${$i}\""; i=$((i+1)); done
        eval "$p $c"
}

mix mybin --conf *.txt

This is portable to any POSIX shell, not just bash, and is able to handle filenames with spaces, special characters, etc:

$ qecho(){ for a; do echo "{$a}"; done; }
$ touch 'a;b' "a'b" "a\\'b" 'a"b' 'a\"b' '(a b)' '(a    b)' 'a
b'
$ mix qecho --conf *
{--conf}
{(a    b)}
{--conf}
{(a b)}
{--conf}
{a
b}
{--conf}
{a"b}
{--conf}
{a'b}
{--conf}
{a;b}
{--conf}
{a\"b}
{--conf}
{a\'b}
0
set -- *.txt

for thing do
    shift
    set -- "$@" --conf "$thing"
done

mybin "$@"

This would use the list of positional parameters ($@) to hold the expanded glob pattern. We then loop over these items and modify $@ by inserting --conf before each. The mybin utility can then be invoked with this list.

The quoting in the code is deliberate to stop the shell from splitting any strings on whitespaces and from expanding any filename globs if they occur as proper parts of the filenames that *.txt matches.

A bash-specific variation:

files=( *.txt )

for thing in "${files[@]}"; do
    args+=( --conf "$thing" )
done

mybin "${args[@]}"

Shorter variations of both of the above. First for /bin/sh:

set --
for thing in *.txt; do
    set -- "$@" --conf "$thing"
done

mybin "$@"

Then for bash:

for thing in *.txt; do
    args+=( --conf "$thing" )
done

mybin "${args[@]}"

As a shell function:

delim_run () {
    cmd=$1
    delim=$2

    shift 2

    for thing do
        shift
        set -- "$@" "$delim" "$thing"
    done

    "$cmd" "$@"
}

You would then be able to do

delim_run mybin --conf *.txt
Kusalananda
  • 14,885
  • 3
  • 41
  • 52