分类 Linux 下的文章

论文图表自动编译


前情提要

老板特别喜欢在论文里面加图加表...加到最后整个论文堆了二三十张图, 一张一张手动处理我显然不太能受得了, 所以就写了个脚本来批量生成图表.

我的图表主要分为两类, 一类是柱状图, 这个主要是利用>$LaTeX$<的pgfplot包来画的, 为了批量修改图标样式, 所以利用\input给每张图配置了统一的模板; 另一类就是Visio了, 很多复杂的样式单纯使用>$LaTeX$<根本画不出来, 或者画起来很麻烦. 不过说起来, 使用>$LaTeX$<画的柱状图比起Excel生成的不知道好看到哪里去了(x

正片开始

我要实现的主要目标是, 能够自动识别我的源文件, 然后只要简单的一个make就能编译出我想要的图表.

对于>$LaTeX$<问题不大, 因为texlive本来就是命令行工具, 但是对于Visio来说就很麻烦了, 不过在万能的github上搜了搜发现了这个, 用了用发现这个本质上还是调起Visio然后自动另存为PDF= =它并不能脱离Visio单独使用
不过好在我的环境是Windows Linux Subsystem, 所以调起win32原生应用并不是一个太大的问题.

Makefile模板

# Common Command Alias
ECHO := @echo -e
MKDIR := mkdir -p
CP := cp
RMF := rm -f
RMD := rm -rf

# Function Command Alias
LATEXMK := latexmk -interaction=nonstopmode -file-line-error --outdir=output
VSDX2PDF := ../utils/OfficeToPDF.exe
PDFCROP := pdfcrop
PDF2EPS := pdftops -eps
LATEXINDENT := latexindent

主要是定义了一些常用的变量, 方便调用和修改.

  • LATEXMK是LateX的编译控制器, 会默认调用pdflatex去编译, 当然你可以通过参数选择你实际需要的编译器;
  • VSDX2PDF是之前介绍的, 可以将Visio自动转成PDF的工具;
  • PDFCROP用于自动裁剪白边, 无论是直接编译出的PDF还是VISIO转成的PDF都有白边, 手动去白边很累的;
  • PDF2EPS用于将PDF的图片转成EPS;
  • LATEXINDENT用于格式化tex文件(可能是我的强迫症

编译LATEX图表

include ../../mk/template.mk

OUTPUTDIR := output

SOURCES := .
TEXFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.tex)))
TARGETS := $(TEXFILES:.tex=)

TEMPLATES := template
TEMPLATEFILES := $(foreach dir,$(SOURCES),$(wildcard $(dir)/$(TEMPLATES)/*.tex))

.PHONY: all install clean clean_aux $(TARGETS)
.PRECIOUS: $(OUTPUTDIR)/%.pdf $(OUTPUTDIR)/%.dvi $(OUTPUTDIR)/%.eps

all: $(TARGETS)
    $(ECHO) "\033[36mBuilding $@...\033[0m"

$(OUTPUTDIR):
    $(MKDIR) $(OUTPUTDIR)

$(TARGETS): %: $(OUTPUTDIR)/%.pdf $(OUTPUTDIR)/%.dvi $(OUTPUTDIR)/%.eps | $(OUTPUTDIR)
    $(ECHO) "\033[36mBuilding $@...\033[0m"
    @$(LATEXMK) -c $@.tex >> /dev/null 2>&1

$(OUTPUTDIR)/%.pdf: %.tex $(TEMPLATEFILES) | $(OUTPUTDIR)
    $(ECHO) "\033[36mBuilding $@...\033[0m"
    $(LATEXMK) -pdf $<

$(OUTPUTDIR)/%.dvi: %.tex $(TEMPLATEFILES) | $(OUTPUTDIR)
    $(ECHO) "\033[36mBuilding $@...\033[0m"
    $(LATEXMK) -dvi $<

$(OUTPUTDIR)/%.eps: $(OUTPUTDIR)/%.pdf | $(OUTPUTDIR)
    $(ECHO) "\033[36mBuilding $@...\033[0m"
    $(PDFCROP) $(OUTPUTDIR)/$(*F).pdf $(OUTPUTDIR)/$(*F).pdf
    $(PDF2EPS) $(OUTPUTDIR)/$(*F).pdf

install: $(TARGETS:%=../pdf/%.pdf) $(TARGETS:%=../dvi/%.dvi) $(TARGETS:%=../eps/%.eps)
    $(ECHO) "\033[36mInstalling Files...\033[0m"

$(TARGETS:%=../pdf/%.pdf): ../pdf/%.pdf: $(OUTPUTDIR)/%.pdf
    $(CP) $< $@

$(TARGETS:%=../dvi/%.dvi): ../dvi/%.dvi: $(OUTPUTDIR)/%.dvi
    $(CP) $< $@

$(TARGETS:%=../eps/%.eps): ../eps/%.eps: $(OUTPUTDIR)/%.eps
    $(CP) $< $@

clean_aux:
    $(ECHO) "\033[36mCleaning aux files...\033[0m"
    $(RMF) \
        $(OUTPUTDIR)/*.fdb_latexmk \
        $(OUTPUTDIR)/*.aux \
        $(OUTPUTDIR)/*.fls \
        $(OUTPUTDIR)/*.log \
        $(OUTPUTDIR)/*.synctex.gz

clean:
    $(ECHO) "\033[36mCleaning all files...\033[0m"
    $(RMD) $(OUTPUTDIR)

流程主要是这样子的:

  1. 扫描SOURCES目录下的.tex文件获得已有的TARGETS
  2. 扫描TEMPLATES目录下.tex文件获得已有的TEMPLATEFILES
  3. TARGETS目标转换为OUTPUTDIR/TARGET.pdf触发实际编译
  4. $(OUTPUTDIR)/%.pdf同时依赖源文件和TEMPLATEFILES, 实现源文件/模板修改后触发所有图表自动更新
  5. $(TARGETS:%=../pdf/%.pdf)的目标是为了将OUTPUTDIR下的输出文件安装到父目录的对应文件夹下

这样就可以实现自动识别目录下的源文件并自动编译了, 同时修改文件可以及时且不遗漏的触发增量编译.

编译VISIO图表

include ../../mk/template.mk

OUTPUTDIR := output

SOURCES := .
VSDXFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.vsdx)))
TARGETS := $(VSDXFILES:.vsdx=)

.PHONY: all install clean $(TARGETS)
.PRECIOUS: $(OUTPUTDIR)/%.pdf $(OUTPUTDIR)/%.eps

all: $(TARGETS)
    $(ECHO) "\033[36mBuilding $@...\033[0m"

$(OUTPUTDIR):
    $(MKDIR) $(OUTPUTDIR)

$(TARGETS): %: $(OUTPUTDIR)/%.pdf $(OUTPUTDIR)/%.eps | $(OUTPUTDIR)
    $(ECHO) "\033[36mBuilding $@...\033[0m"

$(OUTPUTDIR)/%.pdf: %.vsdx | $(OUTPUTDIR)
    $(VSDX2PDF) $< $@

$(OUTPUTDIR)/%.eps: $(OUTPUTDIR)/%.pdf | $(OUTPUTDIR)
    $(PDFCROP) $< $<
    $(PDF2EPS) $<

install: $(TARGETS:%=../pdf/%.pdf) $(TARGETS:%=../eps/%.eps)
    $(ECHO) "\033[36mInstalling Files...\033[0m"

$(TARGETS:%=../pdf/%.pdf): ../pdf/%.pdf: $(OUTPUTDIR)/%.pdf
    $(CP) $< $@

$(TARGETS:%=../eps/%.eps): ../eps/%.eps: $(OUTPUTDIR)/%.eps
    $(CP) $< $@

clean:
    $(ECHO) "\033[36mCleaning all files...\033[0m"
    $(RMD) $(OUTPUTDIR)

VISIO的编译流程与LATEX的类似, 只不过没有模板和DVI文件.

图表根目录

include ../mk/template.mk

SOURCES := BarChart DiscTree Visio 
OUTPUTS := pdf dvi eps

.PHONY: all clean $(SOURCES) $(SOURCES:%=clean_%)

all: $(SOURCES)
    $(ECHO) "\033[36mBuilding $@...\033[0m"

$(SOURCES): | $(OUTPUTS)
    $(ECHO) "\033[36mBuilding $@...\033[0m"
    make -C $@ all 
    make -C $@ install

$(OUTPUTS):
    $(MKDIR) $@

clean: $(SOURCES:%=clean_%)
    $(ECHO) "\033[36mCleaning all files...\033[0m"
    $(RMD) pdf dvi eps

$(SOURCES:%=clean_%):
    make -C $(@:clean_%=%) clean

这里的$(SOURCES:%=clean_%)是为了能使用类似于make clean_BarChart来单独清理BarChart子目录.

将各个子目录编译出的文件合并到根目录上并没有在根目录上来控制而是在各个子目录里install到根目录上来. 因为在根目录上很难做到这一点, 因为在编译开始前我对每个子目录到底有哪些目标文件是未知的, 只有在实际的编译流程开始以后才能知道, make(gnu)提供的控制指令有着很大的局限性, 如果用python编写类似的脚本是可以做到类似的需求的.

总结与思考

虽然说调试整个一套编译脚本花费了一定的时间(还包括论文的自动编译以及其他的一些, 不过都很类似), 但是对于整体效率的提升是非常巨大的.

在这之前, 对于LaTeX图表我需要单独每个编译出PDF, 对于Visio我需要每一个文件都点开然后另存为PDF. 同时, 对于裁白边, 都是通过Adobe Illustrator CC打开PDF, 然后全选内容导出资源为PDF, 非常累, 还可能碰到字体的问题. 此外, 还要再用Adobe Acrobat DC打开裁掉白边的PDF将其另存为eps.

图表比较少, 修改不大的时候, 这一套流程下来还可以接受, 但是图表一多, 简直要疯. 然后, 现在只要打开WSL然后make, 只要花上个厕所的时间, 就可以一键自动编译图表及论文了w

思考

  • WSL能够在运行Linux程序的前提下运行win32原生程序是一大优势
  • 以前编译OpenCV以及ROS的时候不知道为什么还要用cmakepython来控制编译, 现在深入用过一遍make以后觉得主要还是make存在着比较大的局限性

最后:

CLI大法好!

简明ArchLinux安装教程


概述

自用的简明ArchLinux安装教程, 主要参考官方Guide, 部分组件按照自己的喜好来

  • 引导管理采用grub2
  • 网络管理采用systemd-networkd
  • 文件系统用的btrfs

启动ArchISO

略 (这都不会还是立刻右上角吧

网络配置

ArchLinux的安装需要网络, 如果是DHCP的动态网络应该默认就配置好了.

如果是静态配置, 那么将需要手动进行配置, 下面将使用systemd-networkd作为网络管理器, 网络配置采用Arch官方推荐的iproute2而不是经典的net-tools.

检测网络是否联通

ping ip.cn

静态网络配置

确认网卡名称并启动网卡(如果没有启动的话)

ip addr show
ip route show
ip link set $interface up

配置systemd-networkd

'/etc/systemd/network/$interface.network'

[Match]
Name=$interface

[Network]
Address=$address/$prefix
Gateway=$gateway
DNS=$dns_server
systemctl start systemd-networkd.service
systemctl start systemd-resolved.service

临时配置静态IP, 路由和DNS(如果不想用systemd-networkd的话)

ip address add $address/$prefix broadcast + dev $interface
ip route add default via $gateway dev $interface
echo 'nameserver $dns_server' >> /etc/resolv.conf

启用NTP服务(TLS连接需要正确的时间)

timedatectl set-ntp true
timedatectl status

磁盘分区

  • 确定DiskName
fdisk -l
  • 磁盘分区

我一般习惯会分一个swap, 因为使用btrfs所以还要再分一个根卷/, 如果使用EFI则需要再分一个 /efi.

fdisk $disk
g: create GPT partition table
n: add a new partition
d: delete a partition
t: change a partition type
p: print the partition table
w: write table to disk and exit
如果使用的是传统BIOS的话, 需要使用t命令将启动分区的类型设置为BIOS boot(4).
  • 格式化分区
mkfs.ext4 $partition # /
mkswap $partition && swapon $partition # swap
mkfs.btrfs -L $label $partition # / (btrfs)
mkfs.vfat $partition # /efi
  • 挂载分区
mount $partition /mnt
mkdir /mnt/efi && mount $partition /mnt/efi # EFI分区

解压基本系统

  • 修改软件源

/etc/pacman.d/mirrorlist

  • 解压基本系统和安装基本包
pacstrap /mnt base grub openssh sudo # 安装sudo和ssh
pacstrap /mnt vim wget zsh git btrfs-progs  # 其他的一些基本包
  • 生成fstab
genfstab -U /mnt >> /mnt/etc/fstab
  • 复制网络配置(如果之间配置了systemd-networkd的话)
cp /etc/systemd/network/$interface.network /mnt/etc/systemd/network/$interface.network
  • Chroot
arch-chroot /mnt

配置基本系统

  • 配置网络并默认启动SSH(如果之间配置了systemd-networkd的话)
systemctl enable systemd-networkd.service
systemctl enable systemd-resolved.service
systemctl enable sshd.service
  • 配置Root密码
passwd
  • 添加新用户并配置Sudo
groupadd sudoers
useradd -m -G sudoers $user
passwd $user
cat '%sudoers ALL=(ALL) ALL' >> /etc/sudoers
  • 生成Initramfs
mkinitcpio -p linux
  • 配置grub
grub-install --target=i386-pc /dev/sdX
grub-mkconfig -o /boot/grub/grub.cfg
至此, 重启就可以进入到Arch的系统里了

额外的系统配置

  • 设置时区和硬件时钟
ln -sf /usr/share/zoneinfo/$Region/$City /etc/localtime
hwclock --systohc
  • 配置Locale

/etc/locale.gen

locale-gen
echo 'LANG=en_US.UTF-8' > /etc/locale.conf
  • 配置主机名和Hosts
echo $hostname > /etc/hostname

/etc/hosts

127.0.0.1   localhost
::1     localhost

尾声

这样就配置好了ArchLinux的基本系统, 其他的图形配置可以重启以后在SSH里慢慢配置.


在WSL运行32位应用程序的神奇操作


在WSL运行32位应用程序的神奇操作

原理

主要的操作是通过qemu-i386-static模拟来运行x32程序, 然后通过binfmt来指定使用qemu来运行给定x32二进制文件

首先需要开启debian的x32基础支持

dpkg --add-architecture i386
apt-get update
apt-get install libstdc++6:i386

这会在dpkg中添加i386架构, 这样就可以通过apt工具安装i386的包了, 然后安装libstdc++会自动安装libc6/libgcc_s, 这样就可以运行基本的x32程序了.
一般的Linux到这一步就已经可以运行x32的程序了, 但是对于WSL来说还是不行, 使用ldd exec依然会告诉你这不是一个可执行文件, 于是说我们需要安装qemu.

安装qemu

apt-get update
apt-get install qemu-user-static

这样我们就可以通过qemu-i386-static exec来实际的运行我们想要的x32程序了, 但是这样很麻烦, 于是我们可以配置binfmt来自动使用qemu来运行qemu.

配置binfmt

update-binfmts --install i386 /usr/bin/qemu-i386-static --magic '\x7fELF\x01\x01\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x03\x00\x01\x00\x00\x00' --mask '\xff\xff\xff\xff\xff\xff\xff\xfc\xff\xff\xff\xff\xff\xff\xff\xff\xf8\xff\xff\xff\xff\xff\xff\xff'
service binfmt-support start

这样, 我们就可以像在正常Linux里面一样运行x32的程序了. 真是非常神奇的操作_(:3」∠)_

后记

当然每次启动要记得启动一下binfmt服务才行.

service binfmt-support start

Reference: https://github.com/Microsoft/WSL/issues/2468#issuecomment-374904520


如何在Terminal中使用Sock5代理


大部分终端应用, 比如wget, curl都支持http_proxy, https_proxy变量配置HTTP代理.
然而常见的SS使用的则是SOCKS代理, 可以使用privoxy做SOCK->HTTP的转换.
但是相对而言privoxy比较重量级, 因此可以采用polipo做转换.

Mac OSX下可以使用brew安装:

brew install polipo

然后添加代理:

vi /usr/local/opt/polipo/homebrew.mxcl.polipo.plist

在array标签之间添加socksParentProxy选项:

<array>
        <string>/usr/local/opt/polipo/bin/polipo</string>
        <string>socksParentProxy=localhost:1080</string>
</array>

测试并启用代理

#!/bin/sh

export http_proxy=""
export https_proxy=""
curl ip.gs
launchctl unload /usr/local/opt/polipo/homebrew.mxcl.polipo.plist &> /dev/null
launchctl load /usr/local/opt/polipo/homebrew.mxcl.polipo.plist
ping -c 3 127.0.0.1 &> /dev/null
export http_proxy="localhost:8123"
export https_proxy="localhost:8123"
curl ip.gs

保存上述内容到http_proxy.sh文件, 需要代理时source文件既可.

source http_proxy.sh

Parallels Tools 11.0.0.3193 在Linux Kernel 4.2以上内核的修复


主要是参考这篇文章: http://journal.dedasys.com/2015/10/26/parallels-with-ubuntu-15-10/

首先你需要把kmods文件夹的压缩包用tar xvf prl_mod.tar.gz解包.
下面说一下需要修改的两个文件:
第一个文件是kmods/prl_tg/Toolgate/Guest/Linux/prl_tg/prltg.c
增加一个头文件的引用

#include "linux/vmalloc.h"

第二个文件是kmods/prl_fs/SharedFolders/Guest/Linux/prl_fs/inode.c在

-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
 #define compat_follow_link_t void*
 #else
 #define compat_follow_link_t int
 #endif

前新增

+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,2,0)
+#define compat_follow_link_t const char*
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)

并删除

-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)

然后还需要将prlfs_follow_link这个函数做修改:
在函数前新增

+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,2,0)
+static compat_follow_link_t prlfs_follow_link(struct dentry *dentry,
+ void **cookie)
+{
+ return *cookie = do_read_symlink(dentry);
+}
+#else

并在函数的结束点新增

+#endif

完成后使用tar czf打包回去就可以完成安装了.