Ваш контейнер работает как пользовательский (контейнер без root ), построенный на пользовательских пространствах имен .
Для работы пользовательские контейнеры имеют сопоставление uid / gid для преобразования хоста uid / gid в контейнер uid / gid . Общий диапазон хостов для них составляет 2 ^ 32 в ширину (, начиная с 0, являющегося реальным корневым пользователем ). Исходя из этого, выделенный диапазон для контейнера обычно сохраняется на уровне 2^16 (, что совместимо с историческими диапазонами uid ).
Любой хост uid , у которого нет преобразования диапазона в uid внутри контейнера, будет отображаться как none(соотв.:nogroup для gid) внутри пользовательского контейнера. Поскольку корень этого контейнера не имеет прав на такой uid , он не может изменить его, и операция завершится ошибкой, как при запуске обычным пользователем.
Вот ссылка из Proxmox с описанием вашей проблемы:
https://pve.proxmox.com/wiki/Unprivileged_LXC_containers
However you will soon realise that every file and directory will be mapped to "nobody" (uid 65534), which is fine as long as
- you do not have restricted permissions set (only group / user readable files, or accessed directories), and
- you do not want to write files using a specific uid/gid, since all files will be created using the high-mapped (100000+) uids.
Существуют инструменты, предназначенные для преобразования этих диапазонов, поэтому подготовленный макет системного дерева можно сместить в диапазон, подходящий для целевого контейнера. Этот инструмент должен запускаться с хоста (или, по крайней мере, в случае «рекурсивных» контейнеров, контейнер «породил» пространство имен пользователя ). Например:
https://github.com/jirutka/uidmapshift
который является повторной реализацией явно несуществующего uidmapshift проекта nsexec:
https://github.com/fcicq/nsexec
Конечно, вы можете сделать это вручную, рассчитав правильную цель uid:gid и используяchown
(с хоста ). Если есть одно значение и простое сопоставление, это должно быть легко.Вот пример (с использованием запущенного пользовательского контейнера LXC):
Контейнер (под названием buster -amd64):
user@buster-amd64:~$ ls -n test
-rw-r--r--. 1 65534 65534 0 Jan 24 21:09 test
root@buster-amd64:/home/user# chown user:user test
chown: changing ownership of 'test': Operation not permitted
Хост (отображает тот же файл):
user@host:~$ ls -n ~/.local/share/lxc/buster-amd64/rootfs/home/user/test
-rw-r--r--. 1 1000 1000 0 Jan 24 22:09 /home/user/.local/share/lxc/buster-amd64/rootfs/home/user/test
Приведенная ниже команда получает процесс инициализации pid (, который равен 1 в контейнере, но здесь значение pid , как показано на хосте ), работающем в контейнере (любой другой процесс контейнера также будет работать):
user@host:~$ lxc-info -Hpn buster-amd64
22926
user@host:~$ cat /proc/22926/uid_map
0 1410720 65536
Это сопоставление должно было быть определено в конфигурации LXC:
user@host:~$ grep lxc.idmap ~/.local/share/lxc/buster-amd64/config
lxc.idmap = u 0 1410720 65536
lxc.idmap = g 0 1410720 65536
Если uid пользовательского контейнера равен 1000 и файл/каталог должен принадлежать этому пользователю, то uid нового хоста должен быть 1410720 + 1000 = 1411720
На хосте, на этот раз как (реальный)root пользователь:
root@host:~# chown 1411720:1411720 ~user/.local/share/lxc/buster-amd64/rootfs/home/user/test
В случае, если файловая система контейнера (s )не смонтирована напрямую где-то в файловой системе хоста (, например :с использованием резервного хранилища LVM или монтирования tmpfs )и, следовательно, недоступна, это также работает с работающий контейнер (и, вероятно, в любом случае предпочтительнее):
root@host:~# chown 1411720:1411720 /proc/22926/root/home/user/test
А теперь о контейнере:
user@buster-amd64:~$ ls -n test
-rw-r--r--. 1 1000 1000 0 Jan 24 21:09 test
И его пользователь root теперь имеет права на этот файл, потому что он находится в правильном отображении uid / gid .
root@buster-amd64:~# chown root:root ~user/test
root@buster-amd64:~#
На стороне ядра ведется работа над функцией, называемойshiftfs , которая все еще меняет форму , чтобы облегчить эти проблемы, выполняя это преобразование через связанное монтирование.
Вы можете использовать tr
для удаления всего, что не является интересующим вас символом, wc
для подсчета оставшихся символов:
ip address show eth0 | grep 'inet ' | tr -d -c ' ' | wc -m
Это хорошо масштабируется для больших объемов текста, tr
очень эффективно.
Обратите внимание, однако, что в некоторых реализациях tr
, включая GNU tr
, это правильно работает только для одиночных -байтовых символов (, таких как символ пробела ).
Если вы хотите подсчитывать только начальные пробелы, вам понадобится что-то более мощное, чемtr
:
ip address show eth0 | grep 'inet ' | sed 's/[^ ].*$//' | tr -d '\n' | wc -m
Это удаляет каждую часть каждой строки, которая не является начальным пробелом, затем удаляет новые строки и подсчеты.
См. Как подсчитать количество определенных символов в каждой строке? , если вас интересует количество строк.
это читается так, как будто вы действительно хотите удалить начальные пробелы
много способов сделать это, предполагая, что вы хотите сделать это в bash. Я нашел это в
https://www.cyberciti.biz/tips/delete-leading-spaces-from-front-of-each-word.html
echo " This is a test"
# remove leading white space on the output
echo " This is a test" | sed -e 's/^[ \t]*//'
так что в вашем случае вы могли бы сделать
ip address show eth0 | grep 'inet ' | sed -e 's/^[ \t]*//'
также проверьте Как обрезать начальные и конечные пробелы в каждой строке вывода?
Чтобы подсчитать количество пробелов в начале каждой строки, вы можете сделать:
awk -F '[^ ].*' '{print length($1)}'
Который печатает длину (в количестве символов )первого поля, где поля разделены любой последовательностью символов, начинающихся с пробела, отличного от -.
Чтобы сообщить о максимальном количестве пробелов, обнаруженных в начале любой строки ввода (о максимальном отступе ), с GNUwc
:
sed 's/[^[:blank:]].*//' | wc -L
Сообщает об этом количестве пробелов с точки зрения ширины экрана на устройстве отображения, где позиции табуляции разделены на 8 столбцов:
$ printf '\tfoo\n' | sed 's/[^[:blank:]].*//' | wc -L
8
$ printf '\u3000foo\n' | sed 's/[^[:blank:]].*//' | wc -L
2
Символ U+3000 (символ идеографического пробела, классифицируемый как пробел в моей локали )представляет собой символ двойной -ширины, закодированный в 3 байта в UTF -8.
Если вы хотите, чтобы максимальная длина сообщалась в виде количества символов:
sed 's/[^[:blank:]].*//;s/./x/g' | wc -L
(s/./x/g
преобразует каждый символ в каждой строке в x
, который, как мы знаем, имеет ширину отображения 1 ).
Или по количеству байтов:
sed 's/[^[:blank:]].*//' |
LC_ALL=C tr -c '\n' '[x*]' | # convert each byte other than newline to x
wc -L
Вывести количество начальных пробелов:
awk '{print match($0,/[^ ]|$/)-1}' file
match($0,/[^ ]|$/)
соответствует первому не -пробелу([^ ]
)или концу -строки -($
)и возвращает свою позицию.
Распечатать количество пробелов:
awk -F '[ ]' '{print (NF?NF-1:0)}' file
-F '[ ]'
устанавливает в качестве разделителя полей пробел. NF
— количество полей. Тернарное выражение означает :«Если NF не равно 0, выведите NF -1, иначе выведите 0». Это потому, что NF равен 0, если строка пуста.
Я взял пример ниже
`echo " praveen"| grep -o "^ *"| awk '{print length($0)}'`6
output
6
Питон
>>> a=" praveen"
>>> import re
>>> k=re.compile(r'^ *')
>>> m=re.search(k,a)
>>> print len(m.group())
6
>>>
$ ip address show eth0 \
| grep -oP '^\h*(?=inet\h)' \
| wc -m;
Это использует GNU grep с режимом PCRE и ищет любые начальные горизонтальные пробелы, также известные как [[ :пробел :]], за которыми следует inet и еще один пробел. Затем мы передаем его wc, чтобы получить количество символов.
Использование gnu awk с переменной FPATH, установленной на набор пробелов.
$... |
gawk -v FPAT='\\s*' '$0=length($1)""'
Используя сжатие списка python, мы также можем передать grep o/p, чтобы получить количество.
$... | python3 -c 'import sys;print(*[len(l)-len(l.lstrip()) for l in sys.stdin],sep="\n")'
Мы также можем передавать вывод grep в perl.
$..... |
perl -F'\H' -pE '$_=$F[0]=~y///c'
Здесь мы разбиваем запись на негоризонтальные пробелы, а затем заменяем каждый символ в первом поле, в скалярном контексте возвращаем сделанные переводы. Назначьте его записи и включите опцию -p для автопечати.