В bash
без опции dotglob
скрытые файлы игнорируются, если только глобус не запрашивает их явно (, то есть с литералом в начале .
). И .
и ..
не игнорируются (в отличие от того, что делают более разумные оболочки, такие как pdksh
, zsh
или fish
).
При использовании dotglob
игнорируются только .
и ..
, если только шаблон не начинается с .
.
В extglob
bash
добавлена поддержка некоторых из ksh
расширенных операторов глобуса. Однако здесь с ошибкой/ошибкой, когда дело доходит до !(...)
.
В обоих ksh
(, хотя и не вpdksh
)и bash
, @(.*)
— это один из случаев, когда вы явно запрашиваете точечные файлы (, хотя в документации это не проясняется ).
Но в !(.git)
вы не запрашиваете файлы точек. ksh
и zsh
в эмуляции ksh
обрабатывают это правильно, но bash
предполагает, что вы запрашиваете файлы точек. И это включает .
и ..
даже с dotglob
. Таким образом, .git
будет скопирован как часть копирования .
.
Чтобы обойти это, вы можете использовать!([.]git)
(здесь [.]
делает .
неявным )в сочетании с dotglob
или явно исключает .
и ..
с!(.git|.|..)
:
$ bash -O extglob -c 'echo !(.git)'
... foo.foo
$ bash -O extglob -O dotglob -c 'echo !(.git)'
... foo.foo
$ bash -O extglob -O dotglob -c 'echo !([.]git)'
foo.foo
$ bash -O extglob -c 'echo !(.git|.|..)'
foo.foo
В последнем случае я бы все равно добавил параметр dotglob
, потому что bash
в будущей версии может быть исправлено, чтобы не включать точечные файлы здесь, как в других оболочках.
Поскольку я использую zsh
, который имеет свои собственные расширенные операторы (с опцией extendedglob
, не включенной по умолчанию, чтобы сохранить совместимость с Борном; он также поддерживает ksh globs с опцией kshglob
, но они более неудобны ), я бы сделал:
set -o extendedglob # (in my ~/.zshrc)
cp -a -- ^.git(D) target/
(обратите внимание, что -a
подразумевает -r
и вам нужен --
, так как мы не можем гарантировать, что имена файлов не будут начинаться с-
).
^.git
является zsh
эквивалентом ksh !(.git)
. (D)
для включения точечных файлов (, но никогда .
и..
)только для этого шара.
Я нашел способ заставить это работать без подключаемого модуля. По сути, вы можете написать скрипт, обертывающий инструмент gcloud
, и указать параметр ansible_ssh_executable
в этом скрипте, который вы можете определить на уровне инвентаря. Вы должны убедиться, что плагин инвентаризации gcp_compute
идентифицирует хосты по имени, потому что это то, что ожидает gcloud compute ssh
.
Вот скрипт:
#!/bin/sh
set -o errexit
# Wraps the gcloud utility to enable connecting to instances which are behind
# GCP Cloud IAP. Used by setting the `ansible_ssh_executable` setting for a play
# or inventory. Parses out the relevant information from Ansible's call to the
# script and injects into the right places of the gcloud utility.
arg_string="$@"
grep_hostname_regex='[a-z]*[0-9]\{2\}\(live\|test\)'
sed_hostname_regex='[a-z]*[0-9]{2}(live|test)'
target_host=$(
echo "$arg_string\c" | grep -o "$grep_hostname_regex"
)
ssh_args=$(
echo "$arg_string\c" | sed -E "s# ${sed_hostname_regex}.*##"
)
cmd=$(
echo "$arg_string\c" | sed -E "s#.*${sed_hostname_regex} ##"
)
gcloud compute ssh "$target_host" \
--command="$cmd" \
--tunnel-through-iap \
-- $ssh_args
Примечание:
sed
могут отличаться в Linux. После обсужденияhttps://www.reddit.com/r/ansible/comments/e9ve5q/ansible_slow_as_a_hell_with_gcp_iap_any_way_to/я изменил решение, чтобы использовать совместное использование соединения SSH через сокет.
Это в два раза быстрее решения @mat. Поставил на наш PROD. Вот реализация, которая не зависит от шаблонов имен хостов!
Правильным решением является использование хоста Bastion/Jump, потому что команда gcloud
по-прежнему порождает интерпретатор Python, который порождаетssh
-это все еще неэффективно!
ansible.cfg
:
[ssh_connection]
pipelining = True
ssh_executable = misc/gssh.sh
ssh_args =
transfer_method = piped
[privilege_escalation]
become = True
become_method = sudo
[defaults]
interpreter_python = /usr/bin/python
gathering = False
# Somehow important to enable parallel execution...
strategy = free
gssh.sh
:
#!/bin/bash
# ansible/ansible/lib/ansible/plugins/connection/ssh.py
# exec_command(self, cmd, in_data=None, sudoable=True) calls _build_command(self, binary, *other_args) as:
# args = (ssh_executable, self.host, cmd)
# cmd = self._build_command(*args)
# So "host" is next to the last, cmd is the last argument of ssh command.
host="${@: -2: 1}"
cmd="${@: -1: 1}"
# ControlMaster=auto & ControlPath=... speedup Ansible execution 2 times.
socket="/tmp/ansible-ssh-${host}-22-iap"
gcloud_args="
--tunnel-through-iap
--zone=europe-west1-b
--quiet
--no-user-output-enabled
--
-C
-o ControlMaster=auto
-o ControlPersist=20
-o PreferredAuthentications=publickey
-o KbdInteractiveAuthentication=no
-o PasswordAuthentication=no
-o ConnectTimeout=20"
exec gcloud compute ssh "$host" $gcloud_args -o ControlPath="$socket" "$cmd"
ОБНОВЛЕНИЕ Инженер Google ответил, что gcloud
не должны вызываться параллельно ! См. «Gcloud Compute ssh» не может использоваться параллельно
Эксперименты показали, что с Ansible fork=5
я почти всегда сталкивался с ошибкой. С fork=2
я никогда не сталкивался с ним.
ОБНОВЛЕНИЕ 2 Прошло время, и по состоянию на конец 2020 года я могу запустить gcloud compute ssh
параллельно (в WSL. Я сделалfork = 10
)без ошибок блокировки.
Спасибо за публикацию этих @gavenkoa и @matt. Одно из предложений состоит в том, чтобы добавить следующее, чтобы вам не нужно было жестко кодировать зону.
Фрагмент:
ZONE=$(gcloud compute instances list --filter="name:${host}" --format='value(zone)')
gcloud_args="
--tunnel-through-iap \
--zone=${ZONE}