Выполнение команды в bash, переданной как строка, с правильной работой подстановки файла

Я хочу выполнить команду bash, явно переданную в виде строки в bash, которая может обрабатывать корректные действия с шаблоном и переменными расширениями.

Я попробовал команду образца (см. ниже для что мне действительно нужно, что более «требует»):

$ bash -c "files=* ; echo ${files} ; for file in {0..2} ; do echo ${file} ; done"

, и я получаю 4 пустые строки (один из echo ${files} и три из for file in {0..2} ...). Фактически это будет отражать все переменные files и file в вызывающей среде, а не оценивать внутри.

Мне нужна на самом деле комбинация вещей для работы:

Оцените правильно files=* в рабочей среде и каталоге (это будет фактически другой шаблон, но это не имеет значения). Правильно петля над ${files}, с for file in ${files} (вместо for file in {0..2}). Используйте замену строк с помощью do mv $file ${file/-0003/-0002} (вместо do echo ${file}). Это, в сочетании с необходимостью вызова из C ++ с system, является тем, что заставляет меня добавить команду с bash, как и к system("bash -c ...");. Если я не добавляю его, используется оболочка по умолчанию dash, которая не поддерживает замену строки, что дает мне ошибку sh: 1: Bad substitution.

Этот вопрос специфичен.

Кроме того, любая другая альтернатива, которая позволяет мне сделать системный вызов из C ++ и выполнить: 1) использование подстановочных файлов, 2) подстановку строк, 3) правильный цикл, было бы хорошо.

0
задан 8 May 2017 в 17:26

6 ответов

Используйте массив и соответствующее цитирование. Например, данный:

$ ls
file1  file2  file4  file5  file6

, тогда

$ bash -c 'files=(*) ; echo "${files[@]}" ; for file in "${files[@]}" ; do echo "${file/2/3}" ; done'
file1 file2 file4 file5 file6
file1
file3
file4
file5
file6
2
ответ дан 22 May 2018 в 22:48
  • 1
    Это существенно сработало. Теперь мне нужно только обнаружить случай, когда files=(*) фактически не соответствует чему-либо, но сохраняя шаблон файла в переменной, поэтому я не вхожу в последующий цикл. – sancho.s 8 May 2017 в 18:07
  • 2
    @ sancho.s использует shopt -s nullglob, чтобы * не увеличивался до нуля, если ничего не соответствует – muru 8 May 2017 в 18:12

Используйте массив и соответствующее цитирование. Например, данный:

$ ls file1 file2 file4 file5 file6

, тогда

$ bash -c 'files=(*) ; echo "${files[@]}" ; for file in "${files[@]}" ; do echo "${file/2/3}" ; done' file1 file2 file4 file5 file6 file1 file3 file4 file5 file6
2
ответ дан 18 July 2018 в 13:37

Используйте массив и соответствующее цитирование. Например, данный:

$ ls file1 file2 file4 file5 file6

, тогда

$ bash -c 'files=(*) ; echo "${files[@]}" ; for file in "${files[@]}" ; do echo "${file/2/3}" ; done' file1 file2 file4 file5 file6 file1 file3 file4 file5 file6
2
ответ дан 24 July 2018 в 20:13

С двойными кавычками переменные будут расширены в вызывающей оболочке. Используйте одиночные кавычки:

$ cd /; bash -c 'files=* ; echo ${files} ; for file in {0..2} ; do echo ${file} ; done'
bin boot dev etc home lib lib64 lost+found mnt opt proc root run sbin srv sys tmp usr var
0
1
2

Вы должны использовать массивы, но:

На самом деле вы должны fork и exec, если вы хотите вызвать определенную команду с конкретными аргументы:

#include <unistd.h>
#include <cstdio>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    pid_t child = fork();
    if (child == -1) // fork failed
        std::perror("fork");
    else if (child == 0) // child
        execl("/bin/bash", "bash", "-c", "files=* ; echo ${files} ; for file in {0..2} ; do echo ${file} ; done", NULL);
    else // parent
        wait(NULL);
}
1
ответ дан 22 May 2018 в 22:48
  • 1
    Команда, которую вы написали, без двойных кавычек и массивов (как в ответе steeldriver) также хорошо работала в моем случае. Сейчас я проверю следующий момент. – sancho.s 8 May 2017 в 18:10

С двойными кавычками переменные будут расширены в вызывающей оболочке. Используйте одиночные кавычки:

$ cd /; bash -c 'files=* ; echo ${files} ; for file in {0..2} ; do echo ${file} ; done' bin boot dev etc home lib lib64 lost+found mnt opt proc root run sbin srv sys tmp usr var 0 1 2

Вы должны использовать массивы, но:

На самом деле вы должны fork и exec, если вы хотите вызвать определенную команду с конкретными аргументы:

#include <unistd.h> #include <cstdio> #include <sys/types.h> #include <sys/wait.h> int main() { pid_t child = fork(); if (child == -1) // fork failed std::perror("fork"); else if (child == 0) // child execl("/bin/bash", "bash", "-c", "files=* ; echo ${files} ; for file in {0..2} ; do echo ${file} ; done", NULL); else // parent wait(NULL); }
1
ответ дан 18 July 2018 в 13:37

С двойными кавычками переменные будут расширены в вызывающей оболочке. Используйте одиночные кавычки:

$ cd /; bash -c 'files=* ; echo ${files} ; for file in {0..2} ; do echo ${file} ; done' bin boot dev etc home lib lib64 lost+found mnt opt proc root run sbin srv sys tmp usr var 0 1 2

Вы должны использовать массивы, но:

На самом деле вы должны fork и exec, если вы хотите вызвать определенную команду с конкретными аргументы:

#include <unistd.h> #include <cstdio> #include <sys/types.h> #include <sys/wait.h> int main() { pid_t child = fork(); if (child == -1) // fork failed std::perror("fork"); else if (child == 0) // child execl("/bin/bash", "bash", "-c", "files=* ; echo ${files} ; for file in {0..2} ; do echo ${file} ; done", NULL); else // parent wait(NULL); }
1
ответ дан 24 July 2018 в 20:13
  • 1
    Команда, которую вы написали, без двойных кавычек и массивов (как в ответе steeldriver) также хорошо работала в моем случае. Сейчас я проверю следующий момент. – sancho.s 8 May 2017 в 18:10

Другие вопросы по тегам:

Похожие вопросы: