Как автоматически установить bash-скрипт глобально в системе через переменную $PATH?

Рейтинг: -2Ответов: 2Опубликовано: 17.01.2023

К примеру у меня есть скрипт для создания локальной копии сайта (это не особо важно какой скрипт, я привёл этот скрипт в качестве примера)

#!/usr/bin/env bash

set -Eeuo pipefail
trap cleanup SIGINT SIGTERM ERR EXIT

VER="1.0.0"

script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P)

usage() {
  cat <<EOF
  
Copysite  v.$VER

Usage: $(basename "${BASH_SOURCE[0]}") [-h] [-v] [--verbose] -d ./Directory -u example.com

Program based on GNU Wget - designed to create a complete local copy of the site while maintaining the file structure

+---------------------------------------+
| Autor    | fftcc                      |
| License  | GNU GPL v3                 |
| Website  | ff99cc.art                 |
| E-mail   | me@ff99cc.art              |
| Git      | codeberg.org/fftcc         |
| Keyoxide | keyoxide.org/me@ff99cc.art |
+---------------------------------------+

Available options:

-h, --help      Print this help and exit
-v, --version   Print version
--verbose       Print script debug info
-d, --dir       Target directory (by default current directory if no parameter value is passed)
-u, --url       Website address
EOF
  exit
}

ver() {
  cat <<EOF
$VER
EOF
  exit
}

cleanup() {
  trap - SIGINT SIGTERM ERR EXIT
}

msg() {
  echo >&2 -e "${1-}"
}

wget_fn() {
  wget --mirror -p --html-extension --base=./ -k -P "${dir-}" "${url-}"
}

die() {
  local msg=$1
  local code=${2-1} # default exit status 1
  msg "$msg"
  exit "$code"
}

parse_params() {

  while :; do
    case "${1-}" in
    -h | --help) usage ;;
    -v | --version) ver ;;
    --verbose) set -x ;;
    -d | --dir)
      dir="${2-}"
      shift
      ;;
    -u | --url)
      url="${2-}"
      shift
      ;;
    -?*) die "Unknown option: $1" ;;
    *) break ;;
    esac
    shift
  done

  args=("$@")

  # check required params and arguments
  [[ -z "${url-}" ]] && die "Missing required parameter: --url"
  return 0
}

parse_params "$@"

wget_fn ${dir} ${url}

Я хочу написать скрипт install.sh который бы копировал (или скачивал с помощью curl) файл copysite в директорию /usr/bin/. Но проблема в том что не на всех системах есть /usr/bin/, пути могут отличаться и такие пути есть в переменной $PATH. Но вот ещё одна проблема, в $PATH может быть несколько путей. Как выбрать один путь в который устанавливаются приложения глобально для системы?

Edit: В интернете я нашёл следующее решение: создать в домашнем каталоге ~/bin и положить скрипт туда. И добавить этот каталог в переменную $PATH через профиль оболочки

PATH = "$HOME/bin"

Но этом случается скрипт будет глобально исполняемым только для текущего пользователя, что мне не подходит.

Если у кого то есть навык создания cli-app на node js, чтобы помочь мне переписать мой скрипт на js. Это обеспечит кроссплатформенность через npm, я буду только рад. К сожалению у меня нет опыта создания приложений на commanderjs или oclif и нет времени изучать эти инструменты с нуля.

Решение

Спасибо ребятам с англоязычного SO, пусть и не так как мне нужно было изначально. Но они решили вопрос с кроссплатформенностью и с запуском скрипта из любой директории с помощью Nodejs (о чём так же было указано в вопросе).

#!/usr/bin/env node
'use strict';

const commander = require('commander');
const { exec } = require("child_process");
const program = new commander.Command();

program
  .version("1.0.0")
  .description(`Creates a complete local copy of the site while maintaining the file structure

+---------------------------------------+
| Autor    | fftcc                      |
| License  | GNU GPL v3                 |
| Website  | ff99cc.art                 |
| E-mail   | me@ff99cc.art              |
| Git      | codeberg.org/fftcc         |
| Keyoxide | keyoxide.org/me@ff99cc.art |
+---------------------------------------+ `)
  .requiredOption('-u, --url <address>', 'Website address')
  .option('-p, --path <value>', 'Target directory', './');

program.parse(process.argv);

const options = program.opts();

if (options.url) {
        exec(`wget --mirror -p --html-extension --base=./ -k -P ${options.path} ${options.url}`,{shell: '/usr/bin/bash'}, (error, stdout, stderr) => {
            if (error) {
                console.log(`error: ${error.message}`);
                return;
            }
            if (stderr) {
                console.log(`stderr: ${stderr}`);
                return;
            }
            console.log(`stdout: ${stdout}`);
        });
}

Ответы

▲ 1

Идея ставить программу куда-то в систему выглядит простой и для пользователей ранних Windows, но плохо согласуется с Linux. Вы пытаетесь нарушить давно устоявшийся стандарт - вам пытаются объяснить, как на самом деле решаются такие вопросы.

Решений тут четыре:

  • помещать программу пользователю локально. Просто потому, что мало чести, чтобы какой-то там юзерский скрипт с рутовыми правами клали в систему. Кстати, на Windows сейчас многие программы ставятся локально в профиль пользователя и не лезут в Program Files

  • сделать пакеты под все нужные дистрибутивы, то есть, поступить классическим для Линукса способом, не пытаясь ломать стандарты

  • распространять программу в виде контейнера. Внутри контейнера можете вытворять что угодно, снаружи же это будет стандартизированная поставка

  • поместить программу в /opt следуя другому стандарту - "все что не является частью дистрибутива класть в /opt". Пользователи при этом могут сами настроить себе PATH, если им это нужно.

▲ 0

если есть systemd, а это почти все основные дистрибутивы, то просто посмотреть все необходимые пути в выводе команды:

systemd-path

либо более конкретно

$ systemd-path system-binaries
/usr/bin