Bash или Python для движения назад?

Вот полный Vagrantfile, который устанавливает Oh My Zsh на коробку Ubuntu 14.04.2 LTS и устанавливает его в качестве оболочки по умолчанию для стандартного пользователя vagrant.

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

# -*- mode: ruby -*-
# vi: set ft=ruby :

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|

  # Pick a box to use:
  config.vm.box = "ubuntu/trusty64"

  ############################################################
  # Oh My ZSH Install section

  # Install git and zsh prerequisites 
  config.vm.provision :shell, inline: "apt-get -y install git"
  config.vm.provision :shell, inline: "apt-get -y install zsh"

  # Clone Oh My Zsh from the git repo
  config.vm.provision :shell, privileged: false,
    inline: "git clone git://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh"

  # Copy in the default .zshrc config file
  config.vm.provision :shell, privileged: false,
    inline: "cp ~/.oh-my-zsh/templates/zshrc.zsh-template ~/.zshrc"

  # Change the vagrant user's shell to use zsh
  config.vm.provision :shell, inline: "chsh -s /bin/zsh vagrant"

  ############################################################


end

В качестве бонуса вы можете сделать однократную копию файла .zshrc вашего хост-компьютера в поле для бродяги с:

config.vm.provision "file", source: "~/.zshrc", destination: ".zshrc"

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

6
задан sth 18 June 2009 в 21:21
поделиться

11 ответов

Как сказал АльбертоПЛ, сохраняйте строки в формате FIFO для последующего использования - не «возвращайтесь назад». Для этого я бы определенно использовал python вместо bash + sed / awk / что угодно.

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

from collections import deque
line_fifo = deque()
for line in open("test"):
    line_fifo.append(line)
    if len(line_fifo) == 4:
        # "look 3 lines backward"                                               
        if line_fifo[0] == line_fifo[-1] == "@STRING_A\n":
            # get rid of that match
            line_fifo.popleft()
        else:
            # print out the top of the fifo
            print line_fifo.popleft(),
# don't forget to print out the fifo when the file ends
for line in line_fifo: print line,
1
ответ дан 17 December 2019 в 00:13
поделиться

Конечно, Python тоже будет работать. Просто сохраните последние три строки в массиве и проверьте, совпадает ли первый элемент в массиве со значением, которое вы в данный момент читаете. Затем удалите значение и распечатайте текущий массив. Затем вы переместите свои элементы, чтобы освободить место для нового значения, и повторите. Конечно, когда массив заполнен, вам нужно будет убедиться, что вы продолжаете перемещать значения из массива и вставлять новые прочитанные значения, останавливаясь каждый раз, чтобы проверять, соответствует ли первое значение в массиве значению, которое вы в настоящее время читаю.

2
ответ дан 17 December 2019 в 00:13
поделиться

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

То же самое и для Python.

Я бы предоставил собственный сценарий, но это не так. быть протестированным. ; -)

1
ответ дан 17 December 2019 в 00:13
поделиться

Этот код просканирует файл и удалит строки, начинающиеся с маркера. По умолчанию он хранит в памяти только три строки:

from collections import deque

def delete(fp, marker, gap=3):
    """Delete lines from *fp* if they with *marker* and are followed
    by another line starting with *marker* *gap* lines after.
    """
    buf = deque()
    for line in fp:
        if len(buf) < gap:
            buf.append(line)
        else:
            old = buf.popleft()
            if not (line.startswith(marker) and old.startswith(marker)):
                yield old
            buf.append(line)
    for line in buf:
        yield line

Я тестировал его с помощью:

>>> from StringIO import StringIO
>>> fp = StringIO('''a
... b
... xxx 1
... c
... xxx 2
... d
... e
... xxx 3
... f
... g
... h
... xxx 4
... i''')
>>> print ''.join(delete(fp, 'xxx'))
a
b
xxx 1
c
d
e
xxx 3
f
g
h
xxx 4
i
1
ответ дан 17 December 2019 в 00:13
поделиться

Я бы подумал об использовании sed. gnu sed поддерживает определение диапазонов строк. если sed не удастся, тогда есть еще один зверь - awk, и я уверен, что вы можете сделать это с awk.

Хорошо, я чувствую, что мне нужно поставить свой awk POC. Я не мог понять, как использовать адреса sed. Я не пробовал комбинацию awk + sed, но мне кажется, что это излишне.

мой сценарий awk работает следующим образом:

  • Он считывает строки и сохраняет их в 3-строчном буфере

  • после того, как найден желаемый шаблон ( /^data.*/ в моем случае), 3-строчный буфер просматривается, чтобы проверить, был ли виден желаемый шаблон три строки назад

  • , если шаблон был замечен, то 3 строки царапаются

, чтобы быть честно говоря, я бы, наверное, тоже выбрал python, учитывая, что awk действительно неудобен. код AWK следующий:

function max(a, b)
{
    if (a > b)
        return a;
    else
        return b;
}

BEGIN {
    w = 0;  #write index
    r = 0;  #read index
    buf[0, 1, 2];   #buffer

}

END {
    # flush buffer
    # start at read index and print out up to w index
    for (k = r % 3; k  r - max(r - 3, 0); k--) {
        #search in 3 line history buf
        if (match(buf[k % 3], /^data.*/) != 0) {
            # found -> remove lines from history
            # by rewriting them -> adjust write index
            w -= max(r, 3);
        }
    }
    buf[w % 3] = $0;
    w++;
}

/^.*/ {
    # store line into buffer, if the history
    # is full, print out the oldest one.
    if (w > 2) {
        print buf[r % 3];
        r++;
        buf[w % 3] = $0;
    }
    else {
        buf[w] = $0;
    }
    w++;
}
-2
ответ дан 17 December 2019 в 00:13
поделиться

В bash вы можете использовать sort -r filename и tail -n filename для чтения файла в обратном направлении.

$LINES=`tail -n filename | sort -r`
# now iterate through the lines and do your checking
-1
ответ дан 17 December 2019 в 00:13
поделиться

Забавно, что по прошествии стольких часов никто еще не дал решение проблемы, как на самом деле сформулировано (как @John Machin указывает в комментарии) - удалите только ведущий маркер (если за ним следует другой такой маркер на 3 строки вниз), а не всю строку, содержащую его. Это, конечно, несложно - вот небольшой мод, необходимый для забавного решения @ truppo, например:

from itertools import izip, chain
f = "foo.txt"
for third, line in izip(chain("   ", open(f)), open(f)):
    if third.startswith("@STRING_A") and line.startswith("@STRING_A"):
        line = line[len("@STRING_A"):]
    print line,

Конечно, в реальной жизни можно использовать iterator.tee вместо чтения файл дважды, используйте этот код в функции, а не повторяйте константу маркера бесконечно, & c; -).

4
ответ дан 17 December 2019 в 00:13
поделиться

Вот более интересное решение, использующее два итератора с трехэлементным смещением :)

from itertools import izip, chain, tee
f1, f2 = tee(open("foo.txt"))
for third, line in izip(chain("   ", f1), f2):
    if not (third.startswith("@STRING_A") and line.startswith("@STRING_A")):
        print line,
2
ответ дан 17 December 2019 в 00:13
поделиться

Этот «ответ» для лиры ... Я исправлю свой предыдущий комментарий: если стрелка находится в первых 3 строках файла, ваш сценарий либо вызовет ошибку IndexError, либо обратится к строке, которой он не должен получить доступ, иногда с интересными побочными эффектами.

Пример вашего скрипта, вызывающего IndexError:

>>> lines = "@string line 0\nblah blah\n".splitlines(True)
>>> needle = "@string "
>>> for i,line in enumerate(lines):
...     if line.startswith(needle) and lines[i-3].startswith(needle):
...         lines[i-3] = lines[i-3].replace(needle, "")
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
IndexError: list index out of range

, и этот пример показывает не только то, что Земля круглая, но также и то, почему ваше «исправление» к «не удалять целиком» строка "проблема должна была использовать .replace (игла," ", 1) или [len (игла):] вместо .replace (игла," ")

>>> lines = "NEEDLE x NEEDLE y\nnoddle\nnuddle\n".splitlines(True)
>>> needle = "NEEDLE"
>>> # Expected result: no change to the file
... for i,line in enumerate(lines):
...     if line.startswith(needle) and lines[i-3].startswith(needle):
...         lines[i-3] = lines[i-3].replace(needle, "")
...
>>> print ''.join(lines)
 x  y   <<<=== whoops!
noddle
nuddle
        <<<=== still got unwanted newline in here
>>>
0
ответ дан 17 December 2019 в 00:13
поделиться

Взгляните на mapstraction . Это может дать вам больше гибкости при предоставлении карт на основе google, osm, yahoo и т. Д., Однако ваш код не придется менять.

(мой код выше удаляет всю строку, а не только флаг @STRING_A)

Это легко исправить, изменив команду на sed:

sed `awk 'BEGIN{ORS=";"}
/@STRING_A/ {
  if(LAST!="" && LAST+3 >= NR) print LAST "s/@STRING_A//"
  LAST = NR
}' test_file` test_file
0
ответ дан 17 December 2019 в 00:13
поделиться

Это может быть то, что вы ищете?

lines = open('sample.txt').readlines()

needle = "@string "

for i,line in enumerate(lines):
    if line.startswith(needle) and lines[i-3].startswith(needle):
        lines[i-3] = lines[i-3].replace(needle, "")
print ''.join(lines)

это выводит:

string 0 extra text
string 1 extra text
string 2 extra text
string 3 extra text
--replaced --  4 extra text
string 5 extra text
string 6 extra text
@string 7 extra text
string 8 extra text
string 9 extra text
string 10 extra text
-1
ответ дан 17 December 2019 в 00:13
поделиться
Другие вопросы по тегам:

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