Почему sh интерпретирует строку shebang как команду после подстановки $()?

#python #linux #bash #shell

#python #linux #bash #оболочка

Вопрос:

Я хочу установить на свой zsh с помощью скрипта Python, поэтому я следую автоматической установке с github. Он используется sh -c для загрузки сценария установки и выполнения его с помощью sh:

 $ sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended
 

Я хочу сделать это в python using subprocess , поэтому я пишу это:

     args = [
        "sh",
        "-c",
        "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
        "",
        "--unattended"
    ]
    p = subprocess.run(args, check=True)
 

Однако я получил ошибку, и в ней говорится:

 sh 1: #!/bin/sh: Not such file or directory
 

Я думаю, это означает, что sh интерпретирует строку shebang как команду.
Как это может произойти и как этого избежать?
Большое спасибо!!!!

Комментарии:

1. Вам нужно выполнить скрипт с помощью sh -c ‘/home/foo/test, sh’? Где /home/foo представляет фактический путь к скрипту

2. Я редактирую свое описание. Не могли бы вы перепроверить это?

3. Вы пропустили $ раньше (curl… . Вы также должны использовать shell=True for subprocess.run .

4. @CyrillePontvieux Извините, что пропущенный $ — это просто опечатка. В документе подпроцесса python shell=True просто означает sh -c, поэтому я не думаю, что это суть.

Ответ №1:

Что на самом деле делает ваша команда;

  • он выполняется curl в текущей оболочке
  • затем он передает заключенный в кавычки вывод из curl в sh -c

То есть строка внутри двойных кавычек — это текст скрипта, который curl уже был извлечен.

На самом деле ваш код на Python делает что-то другое.

Я бы реорганизовал ее просто

 import requests
import subprocess

r = requests.get('https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh')
subprocess.run(['sh'], input=r.text, text=True)
 

или, возможно, более буквально копирует существующую команду,

 subprocess.run(['sh', '-c', r.text, '', '--unattended'])
 

( requests здесь нет строгой необходимости, если вы не хотите использовать стороннюю библиотеку.

 from urllib3 import PoolManager as u3

r = u3().request('GET', 'https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh')
subprocess.run(['sh', '-c', r.data, '', '--unattended'])
 

но, как вы можете видеть, использование только стандартной библиотеки Python немного более громоздко.)

Ответ №2:

Проблема в том, что вам потребовалось два раунда оценки: один для расширения подстановки команды, затем второй для обработки ее как кода оболочки. Если бы вы использовали одинарные кавычки в своей командной оболочке, вы бы столкнулись с той же проблемой.

Вы можете явно добавить eval :

 args = [
        "sh",
        "-c",
        "eval "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)""
        "",
        "--unattended"
    ]
p = subprocess.run(args, check=True)