Используя awk удалить столбцы, которые стоят "-" в первой строке

Совершенно другой подход :Создайте группу students, дайте каждому учащемуся свою учетную запись с членством в группе в students. Имейте сценарий, который восстанавливает заданный домашний каталог из шаблона в известное хорошее состояние, возможно, удаляя все лишние файлы точек. Расскажите учащимся об этом сценарии.

Если у вас несколько компьютеров, централизуйте этот подход (управление пользователями на одном центральном сервере )и используйте центральный файловый сервер для домашних каталогов учащихся, чтобы каждый учащийся получал один и тот же домашний каталог на любой машине..

Вместе с правильными (базовымиchmod)разрешениями везде это гарантирует, что каждый учащийся сможет сеять хаос только в своем собственном домашнем каталоге и может восстановить его, когда он сломается, возможно, потеряв свои собственные настройки в этом процессе., так что в следующий раз они будут осторожнее.

Кстати, это очень стандартная настройка для многих пользователей кластера машин.

3
17.12.2019, 23:26
5 ответов
$ cat tst.awk
NR == 1 {
    for (i=1; i<=NF; i++) {
        if ($i != "-") {
            f[++numOutFlds] = i
        }
    }
}
{
    for (i=1; i<=numOutFlds; i++) {
        printf "%s%s", $(f[i]), (i<numOutFlds ? OFS : ORS)
    }
}

$ awk -f tst.awk file
M A A
- G -
M - -
3
27.01.2020, 21:11

awkнеявно перебирает входные записи (строки )и файлы, но не поля, которые вы должны делать явно. В вашем случае вам нужно перебрать поля в первой строке (заголовка ), чтобы решить, какие столбцы включить, а затем перебрать поля в каждой строке заголовка (и не заголовке ). чтобы включить нужные столбцы в эту строку.

Вам не ясно, хотите ли вы искать поля заголовка, которые равны(строке )" -" или могут иметь ее как(sub )строка . Я также предполагаю, что у вас есть (все )отдельные вкладки в качестве разделителей полей, а не несколько пробелов, которые были бы более утомительными (и их нельзя отличить визуально от вашего поста ).

awk -F"\t" 'NR==1{for(i=1;i<=NF;i++)s[i]=$i!="-"} {x="";for(i=1;i<=NF;i++)if(s[i])x=x FS $i;print substr(x,2)}'
# for _matches_ "-" instead of _equals_ "-" change $i!="-" to $i!~/-/
# note if a nonheader line has more fields than the header did,
# all extra fields are nonselected (as if their header field was/matched -)

# or (re)use the flags for both what to include _and_ when to terminate the line
awk -F"\t" 'NR==1{t=RS;for(i=NF;i;i--)if(s[i]=($i!="-"?t:""))t=FS} {for(i=1;i<=NF;i++)if(s[i])printf "%s%s",$i,s[i]}'
# some people may consider this too clever
1
27.01.2020, 21:11

Вариация ответа Эда Мортона , который запоминает, каких полей нет -в первой строке по их номеру поля, а затем повторно -формирует каждую запись во входных данных в соответствии с сохраненным индексы в массиве outперед печатью новой записи:

FNR == 1 {
    for (i = 1; i <= NF; ++i)
        if ($i != "-") out[++nf] = i
}

{
    for (i = 1; i <= nf; ++i)
        a[i] = $(out[i])

    $0 = ""

    for (i = 1; i <= nf; ++i)
        $i = a[i]

    print
}

Здесь я немного пожертвовал эффективностью ради удобочитаемости, реконструируя запись в отдельном цикле, а не печатая требуемые поля в одном цикле во втором блоке.

Тестирование:

$ awk -f script.awk file
M A A
- G -
M - -

Запуск с вкладками в качестве разделителя полей вывода:

$ awk -v OFS='\t' -f script.awk file
M       A       A
-       G       -
M       -       -

Слишком длинный -вкладыш, зависящий от того, что входные данные разделены табуляцией -:

$ cut -f "$(awk -v OFS=',' '{ nf=split($0,a); $0=""; for (i=1; i<=nf; ++i) if (a[i]!="-") $(++NF)=i; print; exit }' file)" file
M       A       A
-       G       -
M       -       -

Здесь используется awkдля вывода номеров полей , которые не находятся -в первой строке, в виде списка, разделенного запятыми -. Затем этот список передается в cut -f, который фактически выводит данные из файла. Обратите внимание, что имя файла (здесь простоfile)дается дважды в командной строке, один раз для awk, а затем еще раз для cut.

3
27.01.2020, 21:11

Использование Perl.

Для первой строки постройте массив @Aиндексов, для которых поле не является тире.

Затем для всех строк, включая первую, выведите только те поля из @F, индексы которых находятся в @A.

$ perl -lane '$, = "\t";
    $. == 1 and @A = grep $F[$_] ne "-", 0..$#F;
    print @F[@A];
' file

Выход:

M   A   A
-   G   -
M   -   -
0
27.01.2020, 21:11

Мы можем сделать это, используя sed, хотя код GNU sed в расширенном режиме регулярных выражений, но это просто лекарство от backslashitis.

Метод заключается в создании карты из первой строки. Поля, которые нужно сохранить, обозначаются как x, другие — как тире. Сохраните эту карту в трюме.

Затем для всех строк добавьте эту карту и поместите маркер в BOL.

В цикле мы продолжаем удалять начальное поле текущей строки, если видим \n -И перемещаем маркер на следующее поле.

Зацикливание завершается, когда этот маркер сталкивается с новой строкой между текущей строкой и удерживаемым пробелом, (из-за команды G ).

$ sed -Ee '
    1{
      h
      y/-/\n/
      s/\S+/x/g;s/[[:blank:]]+//g
      y/\n/-/
      x
    }

    G;s/^/\n/

    :a
      s/\n(\S+\s*)(.*\n)x/\1\n\2/
      s/\n(\S+\s*)(.*\n)-/\n\2/
    /\n\n/!ba

    s/\s+$//
' file

Результаты

M       A       A
-       G       -
M       -       -
0
27.01.2020, 21:11

Теги

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