Long Luo's Life Notes

每一天都是奇迹

By Long Luo

春节前在杭州的最后一个夜晚来市中心看钱塘江夜景。

城市因为有了水,才有了生机,河流和湖泊,总是城市里最美的一道风景!

城在水中映,但要成为美不胜收的风景,却并不容易。首先江面不能太窄,要不就成了河景、溪景,也不能太宽,太宽就失之亲切。

看过了这么多城市的江景,唯有黄浦江夜景是不能不看的。

一面是外滩,见证了上海滩沧桑历史,一面是陆家嘴,未来宏图画卷等待书写;一面是古典,一面是现代,交相辉映,相得益彰,尽显江岸美景。

相比之下,钱塘江的夜景缺乏像小蛮腰一样独具特色的地标。不过,钱塘江还有很多没有开发,相信很快就能看到。

杭州,明年见!

By Long Luo at 2018.02.14 in Bingjiang, Hangzhou, China.

By Long Luo

招聘其实本质上是在做风险评估,根据简历和面试确定风险最小的那个人。

其实这个风险评估可以推而广之适用于涉及选择的各个场合。

最近在协助公司招人,也为项目组面试了一些人,对招人也有了一些思考。

为了避免招到不合适的人,将风险降到最低,那么就会从各方面均衡考虑。

从面试官的角度来看,为什么要名校和名企出身的人。

第一,因为名企已经帮你做了一次筛选了,尤其是校招进入BAT的,更是万里挑一,所以风险可以降到最低。

第二,学历其实也是一项筛选。学历可以证明你的学习能力以及把握机会的能力(高考是一次可以改变人生轨迹的机会。)。

第三,如果没有名企和学历的光环,那么你就要用你做成功的事情来证明你自己确实出类拔萃。

综合,应聘者需要用一些东西来证明自己比其他应聘者更优秀,而且实力已经得到证明,而不是到新公司体现和证明。

By Long Luo @2017-11-28 at Shenzhen.

By Long Luo

约瑟夫问题(Josephus problem) 1 是每个学计算机的同学都会遇到的经典编程题,可以考察链表、递归、数学等知识,下面我们就来通过这道题对好好学习下算法和编程吧,Let’s go!

一、约瑟夫问题

据说著名犹太历史学家 \(\textit{Josephus}\) 有过以下的故事:

在罗马人占领乔塔帕特后,\(39\) 个犹太人与 \(\textit{Josephus}\) 及他的朋友躲到一个洞中,\(39\) 个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,\(41\) 个人排成一个圆圈,由第 \(1\) 个人开始报数,每报数到第 \(3\) 人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。

然而 \(\textit{Josephus}\) 和他的朋友并不想遵从这个规则,\(\textit{Josephus}\) 要他的朋友先假装遵从,他将朋友与自己安排在第 \(16\) 个与第 \(31\) 个位置,于是逃过了这场死亡游戏。

二、约瑟夫问题算法分析

约瑟夫问题可用代数分析来求解,将这个问题扩大好了,假设现在您与 \(N\) 个朋友不幸参与了这个游戏,您要如何保护您与您的朋友?

只要画两个圆圈就可以让自己与朋友免于死亡游戏,这两个圆圈内圈是排列顺序,而外圈是自杀顺序,如下图所示:

Josephus Problem

假如使用编程来求解的话,只要将 \(\textit{array}\) 当作环状来处理就可以了,在 \(\textit{array}\) 中由计数 \(1\) 开始,每找到 \(3\) 个无数据区就填入 \(1\) 个计数,直到计数达 \(41\) 为止,然后将 \(\textit{array}\) 由索引 \(1\) 开始列出,就可以得知每个位置的自杀顺序,这就是约瑟夫排列,\(41\) 个人而报数 \(3\) 的约琴夫排列如下所示:

14 36 1 38 15 2 24 30 3 16 34 425 17 5 40 31 6 18 26 7 37 19 8 35 27 9 20 32 10 41 21 11 28 39 12 22 33 13 29 23

由上可知,最后一个自杀的是在第 \(31\) 个位置,而倒数第二个自杀的要排在第 \(16\) 个位置,而之前的人都死光了,所以他们也就不知道约瑟夫与他的朋友并没有遵守游戏规则了。

这是一道经典算法问题,每个学编程的同学都会遇到。一般常见的问法有以下几种:

  1. 输出自杀顺序;
  2. 输出最后一个自杀同学编号。

下面我们就来动手实践下,具体实现代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private static int Josephus(int n, int m) {
int[] peopleArr = new int[n];
for (int i = 0; i < n; i++) {
peopleArr[i] = i + 1;
}

int index = 0;
int length = n;
int count = 1;

while (length > 0) {
if (peopleArr[index % n] > 0) {
if (count % m == 0) {
// 找到要出圈的人,并把圈中人数减1
System.out.print(peopleArr[index % n] + " ");
peopleArr[index % n] = -1;
count = 1;
index++;
length--;
} else {
index++;
count++;
}
} else {
// 遇到空位了,就跳到下一位,但count不加1,也就是这个位置没有报数
index++;
}
}

return index;
}
阅读全文 »

By Long Luo

Linux环境变量问题汇总

测试好的脚本放到 crontab 里就报错: 找不到命令

写好一个脚本,测试没有问题,然后放到crontab 想要定时执行,但是总是报错,去看日志的话显示某些命令找不到,这种一般都是因为PATH环境变量变了导致的

自己在shell命令行下测试的时候当前环境变量就是这个用户的环境变量,可以通过命令:env 看到,脚本放到crontab 里面后一般都加了sudo 这个时候 env 变了。比如你可以在命令行下执行 env 和 sudo env 比较一下就发现他们很不一样

sudo有一个参数 -E (–preserver-env)就是为了解决这个问题的。

这个时候再比较一下

  • env
  • sudo env
  • sudo -E env

大概就能理解这里的区别了。

本文后面的回复中有同学提到了:

第一个问题,sudo -E在集团的容器中貌似是不行的,没有特别好的解,我们最后是通过在要执行的脚本中手动source “/etc/profile.d/dockerenv.sh”才行

我也特意去测试了一下官方的Docker容器,也有同样的问题,/etc/profile.d/dockerenv.sh 中的脚本没有生效,然后debug看了看,主要是因为bashrc中的 . 和 source 不同导致的,不能说没有生效,而是加载 /etc/profile.d/dockerenv.sh 是在一个独立的bash 进程中,加载完毕进程结束,所有加载过的变量都完成了生命周期释放了,类似我文章中的export部分提到的。我尝试把 ~/.bashrc 中的 . /etc/bashrc 改成 source /etc/bashrc , 同时也把 /etc/bashrc 中的 . 改成 source,就可以了,再次进到容器不需要任何操作就能看到所有:/etc/profile.d/dockerenv.sh 中的变量了,所以我们制作镜像的时候考虑改改这里

crontab

docker 容器中admin取不到env参数

docker run的时候带入一堆参数,用root能env中能看到这些参数,admin用户也能看见这些参数,但是通过crond用admin就没法启动应用了,因为读不到这些env。

同样一个命令ssh执行不了, 报找不到命令

比如:

ssh user@ip ” ip a ” 报错: bash: ip: command not found

但是你要是先执行 ssh user@ip 连上服务器后,再执行 ip a 就可以,这里是同一个命令通过两种不同的方式使用,但是环境变量也不一样了。

同样想要解决这个问题的话可以先 ssh 连上服务器,再执行 which ip ; env | grep PATH

$ which ip
/usr/sbin/ip
$ env | grep PATH
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin

很明显这里因为 ip在/usr/sbin下,而/usr/sbin又在PATH变量中,所以可以找到。

那么接下来我们看看

$ssh user@ip "env | grep PATH"
PATH=/usr/local/bin:/usr/bin

很明显这里的PATH比上面的PATH短了一截,/usr/sbin也没有在里面,所以/usr/sbin 下的ip命令自然也找不到了,这里虽然都是同一个用户,但是他们的环境变量还不一样,有点出乎我的意料之外。

主要原因是我们的shell 分为login shell 和 no-login shell , 先ssh 登陆上去再执行命令就是一个login shell,Linux要为这个终端分配资源。

而下面的直接在ssh 里面放执行命令实际上就不需要login,所以这是一个no-login shell.

login shell 和 no-login shell又有什么区别呢?

  • login shell加载环境变量的顺序是:① /etc/profile ② ~/.bash_profile ③ ~/.bashrc ④ /etc/bashrc
  • 而non-login shell加载环境变量的顺序是: ① ~/.bashrc ② /etc/bashrc

也就是nog-login少了前面两步,我们先看后面两步。

下面是一个 .bashrc 的内容:

$ cat .bashrc 
# .bashrc

# Source global definitions
if [ -f /etc/bashrc ]; then
    . /etc/bashrc
fi

基本没有什么内容,它主要是去加载 /etc/bashrc 而他里面也没有看到sbin相关的东西

那我们再看non-login少的两步: ① /etc/profile ② ~/.bash_profile

cat /etc/profile : if [ “$EUID” = “0” ]; then pathmunge /usr/sbin pathmunge /usr/local/sbin else pathmunge /usr/local/sbin after pathmunge /usr/sbin after fi

这几行代码就是把 /usr/sbin 添加到 PATH 变量中,正是他们的区别决定了这里的环境变量不一样。

用一张图来表述他们的结构,箭头代表加载顺序,红框代表不同的shell的初始入口image.png

像 ansible 这种自动化工具,或者我们自己写的自动化脚本,底层通过ssh这种non-login的方式来执行的话,那么都有可能碰到这个问题,如何修复呢?

在 /etc/profile.d/ 下创建一个文件:/etc/profile.d/my_bashenv.sh 内容如下:

$cat /etc/profile.d/my_bashenv.sh 

pathmunge () {
if ! echo $PATH | /bin/egrep -q "(^|:)$1($|:)" ; then
   if [ "$2" = "after" ] ; then
  PATH=$PATH:$1
   else
  PATH=$1:$PATH
   fi
fi
}
 
pathmunge /sbin
pathmunge /usr/sbin
pathmunge /usr/local/sbin
pathmunge /usr/local/bin
pathmunge /usr/X11R6/bin after
 
unset pathmunge

complete -cf sudo
 
    alias chgrp='chgrp --preserve-root'
    alias chown='chown --preserve-root'
    alias chmod='chmod --preserve-root'
    alias rm='rm -i --preserve-root'
    
HISTTIMEFORMAT='[%F %T] '
HISTSIZE=1000
export EDITOR=vim    
export PS1='\n\e[1;37m[\e[m\e[1;32m\u\e[m\e[1;33m@\e[m\e[1;35m\H\e[m \e[4m`pwd`\e[m\e[1;37m]\e[m\e[1;36m\e[m\n\$'

通过前面我们可以看到 /etc/bashrc 总是会去加载 /etc/profile.d/ 下的所有 *.sh 文件,同时我们还可以在这个文件中修改我们喜欢的 shell 配色方案和环境变量等等

脚本前增加如下一行是好习惯

1
#!/bin/bash --login
image-20220505213833017

BASH

1、交互式的登录shell (bash –il xxx.sh) 载入的信息: /etc/profile ~/.bash_profile( -> ~/.bashrc -> /etc/bashrc) ~/.bash_login ~/.profile

2、非交互式的登录shell (bash –l xxx.sh) 载入的信息: /etc/profile ~/.bash_profile ( -> ~/.bashrc -> /etc/bashrc) ~/.bash_login ~/.profile $BASH_ENV

3、交互式的非登录shell (bash –i xxx.sh) 载入的信息: ~/.bashrc ( -> /etc/bashrc)

4、非交互式的非登录shell (bash xxx.sh) 载入的信息: $BASH_ENV

SH

1、交互式的登录shell 载入的信息:

/etc/profile ~/.profile

2、非交互式的登录shell 载入的信息:

/etc/profile ~/.profile

3、交互式的非登录shell 载入的信息:

$ENV

练习验证一下bash、sh和login、non-login

  • sudo ll 或者 sudo cd 是不是都报找不到命令
  • 先sudo bash 然后执行 ll或者cd就可以了
  • 先sudo sh 然后执行 ll或者cd还是报找不到命令
  • sudo env | grep PATH 然后 sudo bash 后再执行 env | grep PATH 看到的PATH环境变量不一样了

找不到ll、cd命令不是因为login/non-login而是因为这两个命令是bash内部定义的,所以sh找不到,通过type -a cd 可以看到一个命令到底是哪里来的

4、非交互式的非登录shell 载入的信息:

nothing

export命令的作用

Linux 中export是一种命令工具通过export命令把shell变量中包含的用户变量导入给子程序.默认情况下子程序仅会继承父程序的环境变量,子程序不会继承父程序的自定义变量,所以需要export让父程序中的自定义变量变成环境变量,然后子程序就能继承过来了。

我们来看一个例子, 有一个变量,名字 abc 内容123 如果没有export ,那么通过bash创建一个新的shell(新shell是之前bash的子程序),在新的shell里面就没有abc这个变量, export之后在新的 shell 里面才可以看到这个变量,但是退出重新login后(产生了一个新的bash,只会加载env)abc变量都不在了

$echo $abc
$abc="123"
$echo $abc
123
$bash
$echo $abc

$exit
exit

$export abc

$echo $abc
123

$bash

$echo $abc
123

一些常见问题

执行好好地shell 脚本换台服务器就:source: not found

source 是bash的一个内建命令(所以你找不到一个/bin/source 这样的可执行文件),也就是他是bash自带的,如果我们执行脚本是这样: sh shell.sh 而shell.sh中用到了source命令的话就会报 source: not found

这是因为bash 和 sh是两个东西,sh是 POSIX shell,你可以把它看成是一个兼容某个规范的shell,而bash是 Bourne-Again shell script, bash是 POSIX shell的扩展,就是bash支持所有符合POSIX shell的规范,但是反过来就不一定了,而这里的 source 恰好就是 bash内建的,不符合 POSIX shell的规范(POSIX shell 中用 . 来代替source)

. (a period)

. filename [arguments]

Read and execute commands from the filename argument in the current shell context. If filename does not contain a slash, the PATH variable is used to find filename. When Bash is not in POSIX mode, the current directory is searched if filename is not found in $PATH. If any arguments are supplied, they become the positional parameters when filename is executed. Otherwise the positional parameters are unchanged. If the -T option is enabled, source inherits any trap on DEBUG; if it is not, any DEBUG trap string is saved and restored around the call to source, and source unsets the DEBUG trap while it executes. If -T is not set, and the sourced file changes the DEBUG trap, the new value is retained when source completes. The return status is the exit status of the last command executed, or zero if no commands are executed. If filename is not found, or cannot be read, the return status is non-zero. This builtin is equivalent to source.

在centos执行好好的脚本放到Ubuntu上就不行了,报语法错误

同上,如果到ubuntu上用 bash shell.sh是可以的,但是sh shell.sh就报语法错误,但是在centos上执行:sh或者bash shell.sh 都可以通过。 在centos上执行 ls -lh /usr/bin/sh 可以看到 /usr/bin/sh link到了 /usr/bin/bash 也就是sh等同于bash,所以都可以通过不足为奇。

但是在ubuntu上执行 ls -lh /usr/bin/sh 可以看到 /usr/bin/sh link到了 /usr/bin/dash , 这就是为什么ubuntu上会报错

source shell.sh 和 bash shell.sh以及 ./shell.sh的区别

source shell.sh就在本shell中展开执行 bash shell.sh表示在本shell启动一个子程序(bash),在子程序中执行 shell.sh (shell.sh中产生的一些环境变量就没法带回父shell进程了), 只需要有读 shell.sh 权限就可以执行 ./shell.sh 跟bash shell.sh类似,但是必须要求shell.sh有rx权限,然后根据shell.sh前面的 #! 后面的指示来确定用bash还是sh

$cat test.sh 
echo $$

$echo $$
2299

$source test.sh 
2299

$bash test.sh 
4037

$./test.sh 
4040

如上实例,只有source的时候进程ID和bash进程ID一样,其它方式都创建了一个新的bash进程,所以ID也变了。

bash test.sh 产生一个新的bash,但是这个新的bash中不会加载 .bashrc 需要加载的话必须 bash -l test.sh.

通过ssh 执行命令(命令前有sudo)的时候报错:sudo: sorry, you must have a tty to run sudo

这是因为 /etc/sudoers (Linux控制sudo行为、权限的配置文件)中指定了 requiretty(Redhat、Fedora默认行为),但是 通过ssh远程执行命令是没有tty的(不需要交互)。 解决办法可以试试 ssh -t or -tt (强制分配tty)或者先修改 /etc/sudoers把 requiretty 删掉或者改成 !requiretty

cp 命令即使使用了 -f force参数,overwrite的时候还是弹出交互信息,必须手工输入Y、yes等

Google搜索一下别人给出的方案是这样 echo yes | cp -rf xxx yyy 算是笨办法,但是没有找到这里为什么-f 不管用。 type -a cp 先确认一下 cp到底是个什么东西:

    #type -a cp
    cp is aliased to `cp -i'
    cp is /usr/bin/cp

这下算是有点清楚了,原来默认cp 都是-i了(-i, –interactive prompt before overwrite (overrides a previous -n option)),看起来就是默认情况下为了保护我们的目录不经意间被修改了。所以真的确认要overwrite的话直接用 /usr/bin/cp -f 就不需要每次yes确认了

重定向

sudo docker logs swarm-agent-master >master.log 2>&1 输出重定向http://www.kissyu.org/2016/12/25/shell%E4%B8%AD%3E%20:dev:null%202%20%3E%20&1%E6%98%AF%E4%BB%80%E4%B9%88%E9%AC%BC%EF%BC%9F/

>/dev/null 2>&1 标准输出丢弃 错误输出丢弃
2>&1 >/dev/null 标准输出丢弃 错误输出屏幕

http://kodango.com/bash-one-liners-explained-part-three

umask

创建文件的默认权限是 666 文件夹是777 但是都要跟 umask做运算(按位减法) 一般umask是002 所以创建出来文件最终是664,文件夹是775,如果umask 是027的话最终文件是 640 文件夹是750 『尽量不要以数字相加减啦!』你应该要这样想(-rw-rw- rw-) – (——–wx)=-rw-rw-r–这样就对啦!不要用十进制的数字喔!够能力的话,用二进制来算,不晓得的话,用 rwx 来算喔!

其它

echo $-   // himBH 

“$-” 中含有“i”代表“交互式shell” “$0”的显示结果为“-bash”,bash前面多个“-”,代表“登录shell”. 没有“i“和“-”的,是“非交互式的非登录shell”

set +o histexpand (! 是history展开符号, histexpand 可以打开或者关闭这个展开符) alias 之后,想要用原来的命令:+alias (命令前加)

bash程序执行,当“$0”是“sh”的时候,则要求下面的代码遵循一定的规范,当不符合规范的语法存在时,则会报错,所以可以这样理解,“sh”并不是一个程序,而是一种标准(POSIX),这种标准,在一定程度上(具体区别见下面的“Things bash has that sh does not”)保证了脚本的跨系统性(跨UNIX系统)

Linux 分 shell变量(set),用户变量(env), shell变量包含用户变量,export是一种命令工具,是显式那些通过export命令把shell变量中包含的用户变量导入给用户变量的那些变量.

set -euxo pipefail //-u unset -e 异常退出 http://www.ruanyifeng.com/blog/2017/11/bash-set.html

引号

shell 中:单引号的处理是比较简单的,被单引号包括的所有字符都保留原有的意思,例如’$a’不会被展开, ’cmd’也不会执行命令;而双引号,则相对比较松,在双引号中,以下几个字符 \(, \`, \ 依然有其特殊的含义,比如\)可以用于变量展开, 反引号`可以执行命令,反斜杠。但是,在双引号包围的字符串里,反斜杠的转义也是有限的,它只能转义$, `, “, (回车)这几个字符,后面如果跟着的不是这几个字符,只不会被黑底,反斜杠会被保留 http://kodango.com/simple-bash-programming-skills-2

su 和 su - 的区别

su命令和su -命令最大的本质区别就是:前者只是切换了root身份,但Shell环境仍然是普通用户的Shell;而后者连用户和Shell环境一起切换成root身份了。只有切换了Shell环境才不会出现PATH环境变量错误。su切换成root用户以后,pwd一下,发现工作目录仍然是普通用户的工作目录;而用su -命令切换以后,工作目录变成root的工作目录了。用echo $PATH命令看一下su和su -以后的环境变量有何不同。以此类推,要从当前用户切换到其它用户也一样,应该使用su -命令。

比如: su admin 会重新加载 ~/.bashrc ,但是不会切换到admin 的home目录。 但是 su - admin 不会重新加载 ~/.bashrc ,但是会切换admin的home目录。

The su command is used to become another user during a login session. Invoked without a username, su defaults to becoming the superuser. The optional argument - may be used to provide an environment similar to what the user would expect had the user logged in directly.

后台任务执行

将任务放到后台,断开ssh后还能运行: “ctrl-Z”将当前任务挂起(实际是发送 SIGTSTP 信号),父进程ssh退出时会给所有子进程发送 SIGHUP;

jobs -l 查看所有job

“disown -h %序号” 让该任务忽略SIGHUP信号(不会因为掉线而终止执行),序号为 Jobs -l 看到的顺序号; “bg”让该任务在后台恢复运行。

shell 调试与参数

为了方便 Debug,有时在启动 Bash 的时候,可以加上启动参数。

  • -n:不运行脚本,只检查是否有语法错误。
  • -v:输出每一行语句运行结果前,会先输出该行语句。
  • -x:每一个命令处理完以后,先输出该命令,再进行下一个命令的处理。
1
2
3
$ bash -n scriptname
$ bash -v scriptname
$ bash -x scriptname

shell 数值运算

bash中数值运算要这样 $(( \(a+\)b )) // declare -i 才是定义一个整型变量

  • 在中括号 [] 内的每个组件都需要有空白键来分隔;
  • 在中括号内的变量,最好都以双引号括号起来;
  • 在中括号内的常数,最好都以单或双引号括号起来。

在bash中为变量赋值的语法是foo=bar,访问变量中存储的数值,其语法为 $foo。 需要注意的是,foo = bar (使用空格隔开)是不能正确工作的,因为解释器会调用程序foo 并将 =bar作为参数。 总的来说,在shell脚本中使用空格会起到分割参数的作用,有时候可能会造成混淆,请务必多加检查。

其它

  • 系统合法的 shell 均写在 /etc/shells 文件中;
  • 用户默认登陆取得的 shell 记录于 /etc/passwd 的最后一个字段;
  • type 可以用来找到运行命令为何种类型,亦可用于与 which 相同的功能 [type -a];
  • 变量主要有环境变量与自定义变量,或称为全局变量与局部变量
  • 使用 env 与 export 可观察环境变量,其中 export 可以将自定义变量转成环境变量;
  • set 可以观察目前 bash 环境下的所有变量;
  • stty -a
  • $? 亦为变量,是前一个命令运行完毕后的回传值。在 Linux 回传值为 0 代表运行成功;
  • bash 的配置文件主要分为 login shell 与 non-login shell。login shell 主要读取 /etc/profile 与 ~/.bash_profile, non-login shell 则仅读取 ~/.bashrc

在bash中进行比较时,尽量使用双方括号 [[ ]] 而不是单方括号 [ ]这样会降低犯错的几率,尽管这样并不能兼容 sh

type

执行顺序(type -a ls 可以查看到顺序):

  1. 以相对/绝对路径运行命令,例如『 /bin/ls 』或『 ./ls 』;
  2. 由 alias 找到该命令来运行;
  3. 由 bash 内建的 (builtin) 命令来运行;
  4. 透过 $PATH 这个变量的顺序搜寻到的第一个命令来运行。

tldr 可以用来查询命令的常用语法,比man简短些,偏case型

参考文章:

关于ansible远程执行的环境变量问题

Bash和Sh的区别

什么是交互式登录 Shell what-is-interactive-and-login-shell

Shell 默认选项 himBH 的解释

useful-documents-about-shell

linux cp实现强制覆盖

https://wangdoc.com/bash/startup.html

编写一个最小的 64 位 Hello World

计算机教育中缺失的一课

MacOS设置环境变量path/paths的完全总结 很详细

最近由于要获取几台Linux服务器的硬件配置信息,就写了一个获取硬件配置信息的脚本代码,可以获取CPU、内存、硬盘等硬件信息。

使用这个脚本文件,就可以一次性输出代码如下所示:

阅读全文 »

By Long Luo

一、

码字和编程都是我的个人爱好

相比小学学了语文之后就开始码字,我真正接触编程还是要等到上了大学之后。

当然作为一名程序员,编程是我至关重要的技能,毕竟,“Talk is cheap, show me the code.”。

码字,换个高大上的说法,也叫写作。不过对于我来说,还是叫码字比较好,毕竟和码代码都差不多,充其量不过是生活工作上的随手记录而已。巴尔扎克不无自豪地说自己是法国社会的书记员,我们只能说是自己苟活一生的书记员。

写作和编程,其实内在是相通的,都是一个创作的过程,都需要事先在大脑中构建一个骨架。然后根据这个骨架,在其中逐渐添加皮和肉,最后形成一个完整丰满的人体。

不过,相比编程需要的绝对严密的逻辑,写作在逻辑上要求就太宽松了,毕竟程序要是出bug了,造成的损失可就大了。

面对一个问题,程序员需要对问题进行透彻的分析,理清其涉及的所有细节,预测可能发生的所有意外与非意外的情况,列出解决方案的所有步骤,以及对解决方案进行尽量全面的测试。

程序员最开心的莫过于程序按照自己设想的方案运行,所有中间分支及数据均按照预定的思路运行,最后得出符合预期的正确的结果。

二、

写作并不神秘,也不高尚,不过是一种倾诉方式,好比有人找朋友聊天,或自言自语,都是抒发内心感受。而写作,不过是将这个过程纯粹化,加以提炼,倾诉成具体的文字罢了。

许多爱好当中,写作最容易被人误会,会特别让人觉得高大上、矫情或伪文艺

其实主要还是理解写作的人知识水平不一致。比如一个人喜欢跑步,他的目标绝对不是跑进奥运会;一个人喜欢唱歌,他也八成没想唱进中国大剧院。平凡人的爱好之所以可贵,在于怡情养性,并通过特定的爱好结识朋友,形成自己稳定的交际生活圈,寻得共同话题和快乐。

和许多其它的爱好一样,无非是消遣、自娱,说得深沉点,叫自我救赎。它与跳广场舞、看电视剧、种花养狗在给予当事人慰藉和快乐上并无二致。

微博上看到一些小学生写的诗歌作文,笔划稚嫩,语言有趣。如果他不饰雕琢,写出本真,比拿腔拿调者更容易成为写作高手。

当今之世,手机人手一部,天下大小事情心里眼中随时过,发表感慨,写点东西已成为抒发内心感受的重要渠道。

比如,心里憋得慌,又找不到对等的倾诉对象,而又非说不可,觉得只有记下来才对得起生活的每一天,慢慢的就会喜欢上写作。

总的来说,写作能够成文,一是对语言文字的把握能力,二是对生活的感受程度。二者缺一不可。

回想写作的意义,对于普通人,就像跳广场舞一样,纯粹是自娱自乐,并以此为信号,向其他人发出真诚邀请。

时间无垠的荒野,有这么几个人,几位志同道不合的朋友,也是一乐。

阅读全文 »
0%